From 272ccded93dc52a31e535d1363f9ae349b13f235 Mon Sep 17 00:00:00 2001 From: Subhash Halder Date: Mon, 8 May 2023 19:21:49 +0530 Subject: [PATCH 1/9] [draft] Added support for quadrant chart --- demos/index.html | 3 + demos/quadrantchart.html | 54 ++++ packages/mermaid/src/config.type.ts | 16 + packages/mermaid/src/defaultConfig.ts | 111 +++++++ .../src/diagram-api/diagram-orchestration.ts | 4 +- .../quadrant-chart/parser/quadrant.jison | 248 +++++++++++++++ .../quadrant-chart/quadrantBuilder.js | 284 ++++++++++++++++++ .../src/diagrams/quadrant-chart/quadrantDb.js | 129 ++++++++ .../quadrant-chart/quadrantDetector.ts | 20 ++ .../quadrant-chart/quadrantDiagram.ts | 13 + .../quadrant-chart/quadrantRenderer.js | 131 ++++++++ .../src/diagrams/quadrant-chart/styles.js | 5 + packages/mermaid/src/themes/theme-base.js | 14 + packages/mermaid/src/themes/theme-dark.js | 14 + packages/mermaid/src/themes/theme-default.js | 14 + packages/mermaid/src/themes/theme-forest.js | 14 + packages/mermaid/src/themes/theme-neutral.js | 14 + 17 files changed, 1087 insertions(+), 1 deletion(-) create mode 100644 demos/quadrantchart.html create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantDetector.ts create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/styles.js diff --git a/demos/index.html b/demos/index.html index da27cdcff..0843cbabe 100644 --- a/demos/index.html +++ b/demos/index.html @@ -54,6 +54,9 @@
  • Pie

  • +
  • +

    Quadrant charts

    +
  • Requirements

  • diff --git a/demos/quadrantchart.html b/demos/quadrantchart.html new file mode 100644 index 000000000..3a340f76a --- /dev/null +++ b/demos/quadrantchart.html @@ -0,0 +1,54 @@ + + + + + + Mermaid Quick Test Page + + + + + +

    Quadrant chart demos

    +
    +    %%{init: {"quadrantChart": {"quadrantPadding": 10}, "theme": "forest", "themeVariables": {"quadrant1TextFill": "blue"}} }%%
    +    quadrantChart
    +      x-axis Urgent --> Not Urgent
    +      y-axis Not Important --> important
    +      quadrant-1 Plan
    +      quadrant-2 Do
    +      quadrant-3 Deligate
    +      quadrant-4 Delete
    +    
    + +
    +    %%{init: {"quadrantChart": {"xAxisPosition": "bottom"} } }%%
    +    quadrantChart
    +      x-axis "Completeness of Vision ❤"
    +      y-axis Ability to Execute
    +      quadrant-1 Leaders
    +      quadrant-2 Challengers
    +      quadrant-3 Niche
    +      quadrant-4 Visionaries
    +      Microsoft: [0.75, 0.75]
    +      Salesforce: [0.55, 0.60]
    +      IBM: [0.51, 0.40]
    +      Incorta: [0.20, 0.30]
    +    
    +
    + + + + diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 545fdbbfb..647657c25 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -27,6 +27,7 @@ export interface MermaidConfig { state?: StateDiagramConfig; er?: ErDiagramConfig; pie?: PieDiagramConfig; + quadrantChart?: QuadrantChartConfig; requirement?: RequirementDiagramConfig; mindmap?: MindmapDiagramConfig; gitGraph?: GitGraphDiagramConfig; @@ -226,6 +227,21 @@ export interface PieDiagramConfig extends BaseDiagramConfig { textPosition?: number; } +export interface QuadrantChartConfig extends BaseDiagramConfig { + quadrantPadding?: number; + xAxisLabelPadding?: number; + yAxisLabelPadding?: number; + xAxisLabelFontSize?: number; + yAxisLabelFontSize?: number; + quadrantLabelFontSize: number; + quadrantTextTopPadding: number; + pointTextPadding: number; + pointLabelFontSize: number; + pointRadius: number; + xAxisPosition: 'top' | 'bottom'; + yAxisPosition: 'left' | 'right'; +} + export interface ErDiagramConfig extends BaseDiagramConfig { titleTopMargin?: number; diagramPadding?: number; diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 9c6d6f46e..24b9b7e8f 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -1280,6 +1280,117 @@ const config: Partial = { textPosition: 0.75, }, + quadrantChart: { + /** + * | Parameter | Description | Type | Required | Values | + * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | + * | quadrantPadding | Padding around the quadrant square | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + quadrantPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | + * | xAxisLabelPadding | Padding around x-axis labels | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + xAxisLabelPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | + * | yAxisLabelPadding | Padding around y-axis labels | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + yAxisLabelPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | xAxisLabelFontSize | x-axis label font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 16 + */ + xAxisLabelFontSize: 16, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | yAxisLabelFontSize | y-axis label font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 16 + */ + yAxisLabelFontSize: 16, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | quadrantLabelFontSize | quadrant title font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 16 + */ + quadrantLabelFontSize: 16, + /** + * | Parameter | Description | Type | Required | Values | + * | ---------------------- | -------------------------------------------------------------------------- | ------- | -------- | ------------------- | + * | quadrantTextTopPadding | quadrant title padding from top if the quadrant is rendered on top | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + quadrantTextTopPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ---------------------- | -------------------------------------- | ------- | -------- | ------------------- | + * | pointTextPadding | padding between point and point label | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + pointTextPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ---------------------- | ---------------------- | ------- | -------- | ------------------- | + * | pointTextPadding | point title font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 12 + */ + pointLabelFontSize: 12, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------- | ------------------------------- | ------- | -------- | ------------------- | + * | pointRadius | radius of the point to be drawn | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + pointRadius: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------- | ------------------------------- | ------- | -------- | ------------------- | + * | xAxisPosition | position of x-axis labels | string | Optional | 'top' or 'bottom' | + * + * **Notes:** + * Default value: top + */ + xAxisPosition: 'top', + /** + * | Parameter | Description | Type | Required | Values | + * | ------------- | ------------------------------- | ------- | -------- | ------------------- | + * | yAxisPosition | position of y-axis labels | string | Optional | 'left' or 'right' | + * + * **Notes:** + * Default value: left + */ + yAxisPosition: 'left', + }, + /** The object containing configurations specific for req diagrams */ requirement: { useWidth: undefined, diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 0d4e6159d..03c04ed09 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -6,6 +6,7 @@ import git from '../diagrams/git/gitGraphDetector.js'; import gantt from '../diagrams/gantt/ganttDetector.js'; import info from '../diagrams/info/infoDetector.js'; import pie from '../diagrams/pie/pieDetector.js'; +import quadrantChart from '../diagrams/quadrant-chart/quadrantDetector.js'; import requirement from '../diagrams/requirement/requirementDetector.js'; import sequence from '../diagrams/sequence/sequenceDetector.js'; import classDiagram from '../diagrams/class/classDetector.js'; @@ -77,6 +78,7 @@ export const addDiagrams = () => { git, stateV2, state, - journey + journey, + quadrantChart ); }; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison new file mode 100644 index 000000000..87d313371 --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison @@ -0,0 +1,248 @@ +/** mermaid + * https://knsv.github.io/mermaid + * (c) 2015 Knut Sveidqvist + * MIT license. + */ +%lex +%options case-insensitive + +%x string +%x string +%x md_string +%x title +%x open_directive +%x type_directive +%x arg_directive +%x close_directive +%x acc_title +%x acc_descr +%x acc_descr_multiline +%x point_start +%x point_x +%x point_y +%% +\%\%\{ { this.begin('open_directive'); return 'open_directive'; } +((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; } +":" { this.popState(); this.begin('arg_directive'); return ':'; } +\}\%\% { this.popState(); this.popState(); return 'close_directive'; } +((?:(?!\}\%\%).|\n)*) return 'arg_directive'; +\%\%(?!\{)[^\n]* /* skip comments */ +[^\}]\%\%[^\n]* /* skip comments */ +[\n\r]+ return 'NEWLINE'; +\%\%[^\n]* /* do nothing */ + +title { this.begin("title");return 'title'; } +(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; } + +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"; + +\s*"x-axis"\s* return 'X-AXIS'; +\s*"y-axis"\s* return 'Y-AXIS'; +\s*\-\-\>\s* return 'AXIS-TEXT-DELIMITER' +\s*"quadrant-1"\s* return 'QUADRANT_1'; +\s*"quadrant-2"\s* return 'QUADRANT_2'; +\s*"quadrant-3"\s* return 'QUADRANT_3'; +\s*"quadrant-4"\s* return 'QUADRANT_4'; + +["][`] { this.begin("md_string");} +<md_string>[^`"]+ { return "MD_STR";} +<md_string>[`]["] { this.popState();} +["] this.begin("string"); +<string>["] this.popState(); +<string>[^"]* return "STR"; + +\s*\:\s*\[\s* {this.begin("point_start"); return 'point_start';} +<point_start>(1)|(0(.\d+)?) {this.begin('point_x'); return 'point_x';} +<point_start>\s*\] {this.popState();} +<point_x>\s*\,\s* {this.popState(); this.begin('point_y');} +<point_y>(1)|(0(.\d+)?) {this.popState(); return 'point_y';} + +"quadrantChart"\s* return 'QUADRANT'; + +\[[0-1].?[0-9]{5}\] return 'POINT_VALUE' +[A-Za-z]+ return 'ALPHA'; +":" return 'COLON'; +\+ return 'PLUS'; +"," return 'COMMA'; +"=" return 'EQUALS'; +\= return 'EQUALS'; +"*" return 'MULT'; +\# return 'BRKT'; +[\_] return 'UNDERSCORE'; +"." return 'DOT'; +"&" return 'AMP'; +\- return 'MINUS'; +[0-9]+ return 'NUM'; +\s return 'SPACE'; +";" return 'SEMI'; +[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION'; +[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| +[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| +[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| +[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]| +[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]| +[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]| +[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]| +[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]| +[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]| +[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]| +[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]| +[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]| +[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]| +[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]| +[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]| +[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]| +[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]| +[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]| +[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]| +[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]| +[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]| +[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]| +[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]| +[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]| +[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]| +[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]| +[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]| +[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]| +[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]| +[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]| +[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]| +[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]| +[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]| +[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]| +[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]| +[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]| +[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]| +[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]| +[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]| +[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]| +[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]| +[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]| +[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]| +[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]| +[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]| +[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]| +[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]| +[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]| +[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]| +[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]| +[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]| +[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]| +[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]| +[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]| +[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]| +[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]| +[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]| +[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]| +[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]| +[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]| +[\uFFD2-\uFFD7\uFFDA-\uFFDC] + return 'UNICODE_TEXT'; +<<EOF>> return 'EOF'; + +/lex + +%start start + +%% /* language grammar */ + +start + : eol start + | directive start + | QUADRANT document + ; + +document + : /* empty */ + | document line + ; + +line + : statement eol { $$ = $1 } + ; + +statement + : + | axisDetails + | quadrantDetails + | points + | title title_value { $$=$2.trim();yy.setDiagramTitle($$); } + | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } + | acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } + | acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);} + | directive + ; + +points + : text point_start point_x point_y {yy.addPoints($1, $3, $4);}; + +axisDetails + : X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);} + | X-AXIS text {yy.setXAxisLeftText($2);} + | Y-AXIS text AXIS-TEXT-DELIMITER text {yy.setYAxisBottomText($2); yy.setYAxisTopText($4);} + | Y-AXIS text {yy.setYAxisBottomText($2);} + ; + +quadrantDetails + : QUADRANT_1 text {yy.setQuadrant1Text($2)} + | QUADRANT_2 text {yy.setQuadrant2Text($2)} + | QUADRANT_3 text {yy.setQuadrant3Text($2)} + | QUADRANT_4 text {yy.setQuadrant4Text($2)} + ; + +directive + : openDirective typeDirective closeDirective + | openDirective typeDirective ':' argDirective closeDirective + ; + +eol + : NEWLINE + | SEMI + | EOF + ; + +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', 'pie'); } + ; + +text: textNoTagsToken + { $$={text:$1, type: 'text'};} + | text textNoTagsToken + { $$={text:$1.text+''+$2, type: $1.type};} + | STR + { $$={text: $1, type: 'text'};} + | MD_STR + { $$={text: $1, type: 'markdown'};} + ; + +alphaNum + : alphaNumToken + {$$=$1;} + | alphaNum alphaNumToken + {$$=$1+''+$2;} + ; + + +alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ; + +textNoTagsToken: alphaNumToken | SPACE | MINUS; + +%% diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js new file mode 100644 index 000000000..c4888cdce --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js @@ -0,0 +1,284 @@ +import { scaleLinear } from 'd3'; + +export class QuadrantBuilder { + totalWidth = 500; + totalHeight = 500; + quadrantPadding = 5; + xAxisLabelPadding = 5; + yAxisLabelPadding = 5; + xAxisLabelFontSize = 16; + yAxisLabelFontSize = 16; + quadrantLabelFontSize = 16; + quadrantTextTopPadding = 5; + pointTextPadding = 5; + pointLabelFontSize = 12; + pointRadius = 5; + points = []; + quadrant1Text = ''; + quadrant2Text = ''; + quadrant3Text = ''; + quadrant4Text = ''; + xAxisLeftText = ''; + xAxisRightText = ''; + yAxisBottomText = ''; + yAxisTopText = ''; + xAxisPosition = 'top'; + yAxisPosition = 'left'; + quadrant1Fill = '#8bc2f3'; + quadrant2Fill = '#faebd7'; + quadrant3Fill = '#00ffff'; + quadrant4Fill = '#f0ffff'; + quadrant1TextFill = '#93690e'; + quadrant2TextFill = '#8644ff'; + quadrant3TextFill = '#e3004d'; + quadrant4TextFill = '#000000'; + pointFill = '#60B19C'; + pointTextFill = '#0000ff'; + xAxisTextFill = '#000000'; + yAxisTextFill = '#000000'; + showXAxis = true; + showYAxis = true; + + constructor() {} + + clear() { + this.points = []; + this.quadrant1Text = ''; + this.quadrant2Text = ''; + this.quadrant3Text = ''; + this.quadrant4Text = ''; + this.xAxisLeftText = ''; + this.xAxisRightText = ''; + this.yAxisBottomText = ''; + this.yAxisTopText = ''; + } + + addPoints(points) { + this.points = this.points.concat([...points]); + } + + set quadrant1Text(text) { + this.quadrant1Text = text; + } + + set quadrant2Text(text) { + this.quadrant2Text = text; + } + + set quadrant3Text(text) { + this.quadrant3Text = text; + } + + set quadrant4Text(text) { + this.quadrant4Text = text; + } + + set xAxisLeftText(text) { + this.xAxisLeftText = text; + } + + set xAxisRightText(text) { + this.xAxisRightText = text; + } + + set yAxisTopText(text) { + this.yAxisTopText = text; + } + + set yAxisBottomText(text) { + this.yAxisBottomText = text; + } + + set totalWidth(width) { + this.totalWidth = width; + } + + set totalHeight(height) { + this.totalHeight = height; + } + + build() { + const showXAxis = (!this.xAxisLeftText && !this.xAxisRightText) ? false: this.showXAxis; + const showYAxis = (!this.yAxisTopText && !this.yAxisBottomText) ? false: this.showYAxis; + const quadrantLeft = + this.quadrantPadding + + ((this.yAxisPosition === 'left' && showYAxis) ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize : 0); + const quadrantTop = + this.quadrantPadding + + ((this.xAxisPosition === 'top' && showXAxis) ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize : 0); + const quadrantWidth = + this.totalWidth - + (this.quadrantPadding * 2 + (showYAxis ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize: 0)); + const quadrantHeight = + this.totalHeight - + (this.quadrantPadding * 2 + (showXAxis ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize: 0)); + + const quadrantHalfWidth = quadrantWidth / 2; + const quadrantHalfHeight = quadrantHeight / 2; + + const axisLabels = []; + + if (this.xAxisLeftText && showXAxis) { + axisLabels.push({ + text: this.xAxisLeftText, + fill: this.xAxisTextFill, + x: quadrantLeft, + y: + this.xAxisPosition === 'top' + ? this.xAxisLabelPadding + : this.xAxisLabelPadding + quadrantTop + quadrantHeight, + fontSize: this.xAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: 0, + }); + } + if (this.xAxisRightText && showXAxis) { + axisLabels.push({ + text: this.xAxisRightText, + fill: this.xAxisTextFill, + x: quadrantLeft + quadrantHalfWidth, + y: + this.xAxisPosition === 'top' + ? this.xAxisLabelPadding + : this.xAxisLabelPadding + quadrantTop + quadrantHeight, + fontSize: this.xAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: 0, + }); + } + + if (this.yAxisBottomText && showYAxis) { + axisLabels.push({ + text: this.yAxisBottomText, + fill: this.yAxisTextFill, + x: + this.yAxisPosition === 'left' + ? this.yAxisLabelPadding + : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, + y: quadrantTop + quadrantHeight, + fontSize: this.yAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: -90, + }); + } + if (this.yAxisTopText && showYAxis) { + axisLabels.push({ + text: this.yAxisTopText, + fill: this.yAxisTextFill, + x: + this.yAxisPosition === 'left' + ? this.yAxisLabelPadding + : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, + y: quadrantTop + quadrantHalfHeight, + fontSize: this.yAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: -90, + }); + } + + const quadrants = [ + { + text: this.quadrant1Text, + textFill: this.quadrant1TextFill, + x: quadrantLeft + quadrantHalfWidth, + y: quadrantTop, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant1Fill, + }, + { + text: this.quadrant2Text, + textFill: this.quadrant2TextFill, + x: quadrantLeft, + y: quadrantTop, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant2Fill, + }, + { + text: this.quadrant3Text, + textFill: this.quadrant3TextFill, + x: quadrantLeft, + y: quadrantTop + quadrantHalfHeight, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant3Fill, + }, + { + text: this.quadrant4Text, + textFill: this.quadrant4TextFill, + x: quadrantLeft + quadrantHalfWidth, + y: quadrantTop + quadrantHalfHeight, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant4Fill, + }, + ]; + quadrants.forEach((quadrant, i) => { + // place the text in the center of the box + if (this.points.length === 0) { + quadrant.text = { + text: quadrant.text, + fill: quadrant.textFill, + x: quadrant.x + quadrant.width / 2, + y: quadrant.y + quadrant.height / 2, + fontSize: this.quadrantLabelFontSize, + verticalPos: 'center', + horizontalPos: 'center', + rotation: 0, + }; + // place the text top of the quadrant square + } else { + quadrant.text = { + text: quadrant.text, + fill: quadrant.textFill, + x: quadrant.x + quadrant.width / 2, + y: quadrant.y + this.quadrantTextTopPadding, + fontSize: this.quadrantLabelFontSize, + verticalPos: 'center', + horizontalPos: 'top', + rotation: 0, + }; + } + delete quadrant.textFill; + }); + + const xAxis = scaleLinear() + .domain([0, 1]) + .range([quadrantLeft, quadrantWidth + quadrantLeft]); + + const yAxis = scaleLinear() + .domain([0, 1]) + .range([quadrantHeight + quadrantTop, quadrantTop]); + + const points = this.points.map((point) => { + const props = { + x: xAxis(point.x), + y: yAxis(point.y), + fill: this.pointFill, + radius: this.pointRadius, + }; + props.text = { + text: point.text, + fill: this.pointTextFill, + x: props.x, + y: props.y + this.pointTextPadding, + verticalPos: 'center', + horizontalPos: 'top', + fontSize: this.pointLabelFontSize, + rotation: 0, + }; + return props; + }); + + return { + points, + quadrants, + axisLabels, + }; + } +} diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js new file mode 100644 index 000000000..a8d33a42d --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js @@ -0,0 +1,129 @@ +import { log } from '../../logger.js'; +import mermaidAPI from '../../mermaidAPI.js'; +import * as configApi from '../../config.js'; +import { sanitizeText } from '../common/common.js'; +import { + setAccTitle, + getAccTitle, + setDiagramTitle, + getDiagramTitle, + getAccDescription, + setAccDescription, + clear as commonClear, +} from '../../commonDb.js'; +import { QuadrantBuilder } from './quadrantBuilder.js'; + +const config = configApi.getConfig(); + +function textSanitizer(text) { + return sanitizeText(text.trim(), config); +} + +const quadrantBuilder = new QuadrantBuilder(); + +function setQuadrant1Text(textObj) { + quadrantBuilder.quadrant1Text = textSanitizer(textObj.text); +} + +function setQuadrant2Text(textObj) { + quadrantBuilder.quadrant2Text = textSanitizer(textObj.text); +} + +function setQuadrant3Text(textObj) { + quadrantBuilder.quadrant3Text = textSanitizer(textObj.text); +} + +function setQuadrant4Text(textObj) { + quadrantBuilder.quadrant4Text = textSanitizer(textObj.text); +} + +function setXAxisLeftText(textObj) { + quadrantBuilder.xAxisLeftText = textSanitizer(textObj.text); +} + +function setXAxisRightText(textObj) { + quadrantBuilder.xAxisRightText = textSanitizer(textObj.text); +} + +function setYAxisTopText(textObj) { + quadrantBuilder.yAxisTopText = textSanitizer(textObj.text); +} + +function setYAxisBottomText(textObj) { + quadrantBuilder.yAxisBottomText = textSanitizer(textObj.text); +} + +function addPoints(textObj, x, y) { + console.log(textObj, x, y); + quadrantBuilder.addPoints([{ x, y, text: textSanitizer(textObj.text) }]); +} + +function setWidth(width) { + quadrantBuilder.totalWidth = width; +} + +function setHeight(height) { + quadrantBuilder.totalHeight = height; +} + +function getQuadrantData() { + const config = configApi.getConfig(); + const { themeVariables, quadrantChart: quadrantChartConfig } = config; + quadrantBuilder.quadrant1Fill = themeVariables.quadrant1Fill; + quadrantBuilder.quadrant2Fill = themeVariables.quadrant2Fill; + quadrantBuilder.quadrant3Fill = themeVariables.quadrant3Fill; + quadrantBuilder.quadrant4Fill = themeVariables.quadrant4Fill; + quadrantBuilder.quadrant1TextFill = themeVariables.quadrant1TextFill; + quadrantBuilder.quadrant2TextFill = themeVariables.quadrant2TextFill; + quadrantBuilder.quadrant3TextFill = themeVariables.quadrant3TextFill; + quadrantBuilder.quadrant4TextFill = themeVariables.quadrant4TextFill; + quadrantBuilder.pointFill = themeVariables.quadrantPointFill; + quadrantBuilder.pointTextFill = themeVariables.quadrantPointTextFill; + quadrantBuilder.xAxisTextFill = themeVariables.quadrantXAxisTextFill; + quadrantBuilder.yAxisTextFill = themeVariables.quadrantYAxisTextFill; + quadrantBuilder.quadrantPadding = quadrantChartConfig.quadrantPadding; + quadrantBuilder.xAxisLabelPadding = quadrantChartConfig.xAxisLabelPadding; + quadrantBuilder.yAxisLabelPadding = quadrantChartConfig.yAxisLabelPadding; + quadrantBuilder.xAxisLabelFontSize = quadrantChartConfig.xAxisLabelFontSize; + quadrantBuilder.yAxisLabelFontSize = quadrantChartConfig.yAxisLabelFontSize; + quadrantBuilder.quadrantLabelFontSize = quadrantChartConfig.quadrantLabelFontSize; + quadrantBuilder.quadrantTextTopPadding = quadrantChartConfig.quadrantTextTopPadding; + quadrantBuilder.pointTextPadding = quadrantChartConfig.pointTextPadding; + quadrantBuilder.pointLabelFontSize = quadrantChartConfig.pointLabelFontSize; + quadrantBuilder.pointRadius = quadrantChartConfig.pointRadius; + quadrantBuilder.xAxisPosition = quadrantChartConfig.xAxisPosition; + quadrantBuilder.yAxisPosition = quadrantChartConfig.yAxisPosition; + return quadrantBuilder.build(); +} + +export const parseDirective = function (statement, context, type) { + mermaidAPI.parseDirective(this, statement, context, type); +}; + +const clear = function () { + quadrantBuilder.clear(); + commonClear(); +}; + +export default { + setWidth, + setHeight, + setQuadrant1Text, + setQuadrant2Text, + setQuadrant3Text, + setQuadrant4Text, + setXAxisLeftText, + setXAxisRightText, + setYAxisTopText, + setYAxisBottomText, + addPoints, + getQuadrantData, + parseDirective, + clear, + setAccTitle, + getAccTitle, + setDiagramTitle, + getDiagramTitle, + getAccDescription, + setAccDescription, +}; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDetector.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDetector.ts new file mode 100644 index 000000000..06d02356b --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDetector.ts @@ -0,0 +1,20 @@ +import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-api/types.js'; + +const id = 'quadrantChart'; + +const detector: DiagramDetector = (txt) => { + return txt.match(/^\s*quadrantChart/) !== null; +}; + +const loader = async () => { + const { diagram } = await import('./quadrantDiagram.js'); + return { id, diagram }; +}; + +const plugin: ExternalDiagramDefinition = { + id, + detector, + loader, +}; + +export default plugin; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts new file mode 100644 index 000000000..ec3d6ed20 --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts @@ -0,0 +1,13 @@ +import { DiagramDefinition } from '../../diagram-api/types.js'; +// @ts-ignore: TODO Fix ts errors +import parser from './parser/quadrant.jison'; +import db from './quadrantDb.js'; +import styles from './styles.js'; +import renderer from './quadrantRenderer.js'; + +export const diagram: DiagramDefinition = { + parser, + db, + renderer, + styles, +}; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js new file mode 100644 index 000000000..91936fb82 --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js @@ -0,0 +1,131 @@ +import { select, scaleLinear } from 'd3'; +import * as configApi from '../../config.js'; +import { log } from '../../logger.js'; + +import { configureSvgSize } from '../../setupGraphViewbox.js'; + +export const draw = (txt, id, _version, diagObj) => { + + + function getDominantBaseLine(horizintalPos) { + return horizintalPos === 'top' ? 'text-before-edge' : 'middle'; + } + + function getTextAnchor(verticalPos) { + return verticalPos === 'left' ? 'start' : 'middle'; + } + + function getTransformation(data) { + return `translate(${data.x}, ${data.y}) rotate(${data.rotation || 0})`; + } + + const conf = configApi.getConfig(); + log.debug('Rendering info diagram\n' + txt); + + 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'); + + const svg = root.select(`[id="${id}"]`); + + const group = svg.append('g').attr('class', 'main'); + + + // const bounds = svg.node().getBox(); + // const width = bounds.width + padding * 2; + // const height = bounds.height + padding * 2; + const width = 500; + const height = 500; + + diagObj.db.setHeight(height); + diagObj.db.setWidth(width); + + svg.attr('width', width); + svg.attr('height', height); + + const quadrantData = diagObj.db.getQuadrantData(); + + const quadrantsGroup = group.append('g').attr('class', 'quadrants'); + const dataPointGroup = group.append('g').attr('class', 'data-points'); + const labelGroup = group.append('g').attr('class', 'labels'); + + const quadrants = quadrantsGroup + .selectAll('g.quadrant') + .data(quadrantData.quadrants) + .enter() + .append('g') + .attr('class', 'quadrant'); + + quadrants + .append('rect') + .attr('x', data => data.x) + .attr('y', data => data.y) + .attr('width', data => data.width) + .attr('height', data => data.height) + .attr('fill', data => data.fill); + + quadrants + .append('text') + .attr('x', 0) + .attr('y', 0) + .attr('fill', data => data.text.fill) + .attr('font-size', data => data.text.fontSize) + .attr('dominant-baseline', data => getDominantBaseLine(data.text.horizontalPos)) + .attr('text-anchor', data => getTextAnchor(data.text.verticalPos)) + .attr('transform', data => getTransformation(data.text)) + .text(data => data.text.text); + + const labels = labelGroup + .selectAll('g.label') + .data(quadrantData.axisLabels) + .enter() + .append('g') + .attr('class', 'label') + + labels + .append('text') + .attr('x', 0) + .attr('y', 0) + .text(data => data.text) + .attr('fill', data => data.fill) + .attr('font-size', data => data.fontSize) + .attr('dominant-baseline', data => getDominantBaseLine(data.horizontalPos)) + .attr('text-anchor', data => getTextAnchor(data.verticalPos)) + .attr('transform', data => getTransformation(data)) + + const dataPoints = dataPointGroup + .selectAll('g.data-point') + .data(quadrantData.points) + .enter() + .append('g') + .attr('class', 'data-point') + + dataPoints + .append('circle') + .attr('cx', data => data.x) + .attr('cy', data => data.y) + .attr('r', data => data.radius) + .attr('fill', data => data.fill); + + dataPoints + .append('text') + .attr('x', 0) + .attr('y', 0) + .text(data => data.text.text) + .attr('fill', data => data.text.fill) + .attr('font-size', data => data.text.fontSize) + .attr('dominant-baseline', data => getDominantBaseLine(data.text.horizontalPos)) + .attr('text-anchor', data => getTextAnchor(data.text.verticalPos)) + .attr('transform', data => getTransformation(data.text)) +}; + +export default { + draw, +}; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/styles.js b/packages/mermaid/src/diagrams/quadrant-chart/styles.js new file mode 100644 index 000000000..fc7ab4bfd --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/styles.js @@ -0,0 +1,5 @@ +const getStyles = (options) => +` +`; + +export default getStyles; diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index 6e26e12a2..30bbfd5b6 100644 --- a/packages/mermaid/src/themes/theme-base.js +++ b/packages/mermaid/src/themes/theme-base.js @@ -220,6 +220,20 @@ class Theme { this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; + /* quadrant-graph */ + this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; + this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); + this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); + this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); + this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; + this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; + this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor; diff --git a/packages/mermaid/src/themes/theme-dark.js b/packages/mermaid/src/themes/theme-dark.js index 7298f3a0c..41c624588 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -226,6 +226,20 @@ class Theme { this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; + /* quadrant-graph */ + this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; + this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); + this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); + this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); + this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; + this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; + this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + /* class */ this.classText = this.primaryTextColor; diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index b29e93862..091240af0 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -247,6 +247,20 @@ class Theme { this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; + /* quadrant-graph */ + this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; + this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); + this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); + this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); + this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; + this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; + this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor; diff --git a/packages/mermaid/src/themes/theme-forest.js b/packages/mermaid/src/themes/theme-forest.js index dbff069b7..0c4af6ec3 100644 --- a/packages/mermaid/src/themes/theme-forest.js +++ b/packages/mermaid/src/themes/theme-forest.js @@ -215,6 +215,20 @@ class Theme { this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; + /* quadrant-graph */ + this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; + this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); + this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); + this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); + this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; + this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; + this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor; diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index bc0725276..1f069c4fd 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -246,6 +246,20 @@ class Theme { this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; + /* quadrant-graph */ + this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; + this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); + this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); + this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); + this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; + this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; + this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor; From 9a7dbab5ed96eab49632a9d0eb12eef3ad9f22ba Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Tue, 9 May 2023 19:28:17 +0530 Subject: [PATCH 2/9] Converted files to typescript and added proper types --- demos/quadrantchart.html | 2 +- packages/mermaid/src/config.type.ts | 12 +- packages/mermaid/src/defaultConfig.ts | 31 ++ .../quadrant-chart/parser/quadrant.jison | 174 +++----- .../quadrant-chart/quadrantBuilder.js | 284 ------------- .../quadrant-chart/quadrantBuilder.ts | 385 ++++++++++++++++++ .../{quadrantDb.js => quadrantDb.ts} | 56 +-- .../quadrant-chart/quadrantDiagram.ts | 3 +- .../quadrant-chart/quadrantRenderer.js | 131 ------ .../quadrant-chart/quadrantRenderer.ts | 144 +++++++ .../src/diagrams/quadrant-chart/styles.js | 5 - packages/mermaid/src/themes/theme-base.js | 24 +- packages/mermaid/src/themes/theme-dark.js | 24 +- packages/mermaid/src/themes/theme-default.js | 24 +- packages/mermaid/src/themes/theme-forest.js | 24 +- packages/mermaid/src/themes/theme-neutral.js | 24 +- 16 files changed, 730 insertions(+), 617 deletions(-) delete mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts rename packages/mermaid/src/diagrams/quadrant-chart/{quadrantDb.js => quadrantDb.ts} (61%) delete mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts delete mode 100644 packages/mermaid/src/diagrams/quadrant-chart/styles.js diff --git a/demos/quadrantchart.html b/demos/quadrantchart.html index 3a340f76a..63a279bba 100644 --- a/demos/quadrantchart.html +++ b/demos/quadrantchart.html @@ -27,7 +27,7 @@ </pre> <pre class="mermaid"> - %%{init: {"quadrantChart": {"xAxisPosition": "bottom"} } }%% + %%{init: {"quadrantChart": {"xAxisPosition": "bottom", "chartWidth": 600, "chartHeight": 600} } }%% quadrantChart x-axis "Completeness of Vision ❤" y-axis Ability to Execute diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 647657c25..3820f2c30 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -228,11 +228,13 @@ export interface PieDiagramConfig extends BaseDiagramConfig { } export interface QuadrantChartConfig extends BaseDiagramConfig { - quadrantPadding?: number; - xAxisLabelPadding?: number; - yAxisLabelPadding?: number; - xAxisLabelFontSize?: number; - yAxisLabelFontSize?: number; + chartWidth: number; + chartHeight: number; + quadrantPadding: number; + xAxisLabelPadding: number; + yAxisLabelPadding: number; + xAxisLabelFontSize: number; + yAxisLabelFontSize: number; quadrantLabelFontSize: number; quadrantTextTopPadding: number; pointTextPadding: number; diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 24b9b7e8f..ec3f55cb5 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -1281,6 +1281,24 @@ const config: Partial<MermaidConfig> = { }, quadrantChart: { + /** + * | Parameter | Description | Type | Required | Values | + * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | + * | chartWidth | Width of the chart | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 500 + */ + chartWidth: 500, + /** + * | Parameter | Description | Type | Required | Values | + * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | + * | chartHeight | Height of the chart | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 500 + */ + chartHeight: 500, /** * | Parameter | Description | Type | Required | Values | * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | @@ -1389,6 +1407,19 @@ const config: Partial<MermaidConfig> = { * Default value: left */ yAxisPosition: 'left', + /** + * | Parameter | Description | Type | Required | Values | + * | ----------- | ----------- | ------- | -------- | ----------- | + * | useMaxWidth | See Notes | boolean | Required | true, false | + * + * **Notes:** + * + * When this flag is set to true, the diagram width is locked to 100% and scaled based on + * available space. If set to false, the diagram reserves its absolute width. + * + * Default value: true + */ + useMaxWidth: true, }, /** The object containing configurations specific for req diagrams */ diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison index 87d313371..ee8cf2ab2 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison @@ -21,130 +21,68 @@ %x point_x %x point_y %% -\%\%\{ { 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\r]+ return 'NEWLINE'; -\%\%[^\n]* /* do nothing */ +\%\%\{ { 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\r]+ return 'NEWLINE'; +\%\%[^\n]* /* do nothing */ -title { this.begin("title");return 'title'; } -<title>(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; } +title { this.begin("title");return 'title'; } +<title>(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; } -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"; +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"; -\s*"x-axis"\s* return 'X-AXIS'; -\s*"y-axis"\s* return 'Y-AXIS'; -\s*\-\-\>\s* return 'AXIS-TEXT-DELIMITER' -\s*"quadrant-1"\s* return 'QUADRANT_1'; -\s*"quadrant-2"\s* return 'QUADRANT_2'; -\s*"quadrant-3"\s* return 'QUADRANT_3'; -\s*"quadrant-4"\s* return 'QUADRANT_4'; +\s*"x-axis"\s* return 'X-AXIS'; +\s*"y-axis"\s* return 'Y-AXIS'; +\s*\-\-\>\s* return 'AXIS-TEXT-DELIMITER' +\s*"quadrant-1"\s* return 'QUADRANT_1'; +\s*"quadrant-2"\s* return 'QUADRANT_2'; +\s*"quadrant-3"\s* return 'QUADRANT_3'; +\s*"quadrant-4"\s* return 'QUADRANT_4'; -["][`] { this.begin("md_string");} -<md_string>[^`"]+ { return "MD_STR";} -<md_string>[`]["] { this.popState();} -["] this.begin("string"); -<string>["] this.popState(); -<string>[^"]* return "STR"; +["][`] { this.begin("md_string");} +<md_string>[^`"]+ { return "MD_STR";} +<md_string>[`]["] { this.popState();} +["] this.begin("string"); +<string>["] this.popState(); +<string>[^"]* return "STR"; -\s*\:\s*\[\s* {this.begin("point_start"); return 'point_start';} -<point_start>(1)|(0(.\d+)?) {this.begin('point_x'); return 'point_x';} -<point_start>\s*\] {this.popState();} -<point_x>\s*\,\s* {this.popState(); this.begin('point_y');} -<point_y>(1)|(0(.\d+)?) {this.popState(); return 'point_y';} +\s*\:\s*\[\s* {this.begin("point_start"); return 'point_start';} +<point_start>(1)|(0(.\d+)?) {this.begin('point_x'); return 'point_x';} +<point_start>\s*\] {this.popState();} +<point_x>\s*\,\s* {this.popState(); this.begin('point_y');} +<point_y>(1)|(0(.\d+)?) {this.popState(); return 'point_y';} -"quadrantChart"\s* return 'QUADRANT'; +"quadrantChart"\s* return 'QUADRANT'; -\[[0-1].?[0-9]{5}\] return 'POINT_VALUE' -[A-Za-z]+ return 'ALPHA'; -":" return 'COLON'; -\+ return 'PLUS'; -"," return 'COMMA'; -"=" return 'EQUALS'; -\= return 'EQUALS'; -"*" return 'MULT'; -\# return 'BRKT'; -[\_] return 'UNDERSCORE'; -"." return 'DOT'; -"&" return 'AMP'; -\- return 'MINUS'; -[0-9]+ return 'NUM'; -\s return 'SPACE'; -";" return 'SEMI'; -[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION'; -[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| -[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| -[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| -[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]| -[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]| -[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]| -[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]| -[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]| -[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]| -[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]| -[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]| -[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]| -[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]| -[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]| -[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]| -[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]| -[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]| -[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]| -[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]| -[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]| -[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]| -[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]| -[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]| -[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]| -[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]| -[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]| -[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]| -[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]| -[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]| -[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]| -[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]| -[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]| -[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]| -[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]| -[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]| -[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]| -[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]| -[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]| -[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]| -[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]| -[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]| -[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]| -[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]| -[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]| -[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]| -[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]| -[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]| -[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]| -[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]| -[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]| -[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]| -[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]| -[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]| -[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]| -[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]| -[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]| -[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]| -[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]| -[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]| -[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]| -[\uFFD2-\uFFD7\uFFDA-\uFFDC] - return 'UNICODE_TEXT'; -<<EOF>> return 'EOF'; +\[[0-1].?[0-9]{5}\] return 'POINT_VALUE' +[A-Za-z]+ return 'ALPHA'; +":" return 'COLON'; +\+ return 'PLUS'; +"," return 'COMMA'; +"=" return 'EQUALS'; +\= return 'EQUALS'; +"*" return 'MULT'; +\# return 'BRKT'; +[\_] return 'UNDERSCORE'; +"." return 'DOT'; +"&" return 'AMP'; +\- return 'MINUS'; +[0-9]+ return 'NUM'; +\s return 'SPACE'; +";" return 'SEMI'; +[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION'; +<<EOF>> return 'EOF'; /lex @@ -241,7 +179,7 @@ alphaNum ; -alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ; +alphaNumToken : PUNCTUATION | AMP | NUM| ALPHA | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ; textNoTagsToken: alphaNumToken | SPACE | MINUS; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js deleted file mode 100644 index c4888cdce..000000000 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.js +++ /dev/null @@ -1,284 +0,0 @@ -import { scaleLinear } from 'd3'; - -export class QuadrantBuilder { - totalWidth = 500; - totalHeight = 500; - quadrantPadding = 5; - xAxisLabelPadding = 5; - yAxisLabelPadding = 5; - xAxisLabelFontSize = 16; - yAxisLabelFontSize = 16; - quadrantLabelFontSize = 16; - quadrantTextTopPadding = 5; - pointTextPadding = 5; - pointLabelFontSize = 12; - pointRadius = 5; - points = []; - quadrant1Text = ''; - quadrant2Text = ''; - quadrant3Text = ''; - quadrant4Text = ''; - xAxisLeftText = ''; - xAxisRightText = ''; - yAxisBottomText = ''; - yAxisTopText = ''; - xAxisPosition = 'top'; - yAxisPosition = 'left'; - quadrant1Fill = '#8bc2f3'; - quadrant2Fill = '#faebd7'; - quadrant3Fill = '#00ffff'; - quadrant4Fill = '#f0ffff'; - quadrant1TextFill = '#93690e'; - quadrant2TextFill = '#8644ff'; - quadrant3TextFill = '#e3004d'; - quadrant4TextFill = '#000000'; - pointFill = '#60B19C'; - pointTextFill = '#0000ff'; - xAxisTextFill = '#000000'; - yAxisTextFill = '#000000'; - showXAxis = true; - showYAxis = true; - - constructor() {} - - clear() { - this.points = []; - this.quadrant1Text = ''; - this.quadrant2Text = ''; - this.quadrant3Text = ''; - this.quadrant4Text = ''; - this.xAxisLeftText = ''; - this.xAxisRightText = ''; - this.yAxisBottomText = ''; - this.yAxisTopText = ''; - } - - addPoints(points) { - this.points = this.points.concat([...points]); - } - - set quadrant1Text(text) { - this.quadrant1Text = text; - } - - set quadrant2Text(text) { - this.quadrant2Text = text; - } - - set quadrant3Text(text) { - this.quadrant3Text = text; - } - - set quadrant4Text(text) { - this.quadrant4Text = text; - } - - set xAxisLeftText(text) { - this.xAxisLeftText = text; - } - - set xAxisRightText(text) { - this.xAxisRightText = text; - } - - set yAxisTopText(text) { - this.yAxisTopText = text; - } - - set yAxisBottomText(text) { - this.yAxisBottomText = text; - } - - set totalWidth(width) { - this.totalWidth = width; - } - - set totalHeight(height) { - this.totalHeight = height; - } - - build() { - const showXAxis = (!this.xAxisLeftText && !this.xAxisRightText) ? false: this.showXAxis; - const showYAxis = (!this.yAxisTopText && !this.yAxisBottomText) ? false: this.showYAxis; - const quadrantLeft = - this.quadrantPadding + - ((this.yAxisPosition === 'left' && showYAxis) ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize : 0); - const quadrantTop = - this.quadrantPadding + - ((this.xAxisPosition === 'top' && showXAxis) ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize : 0); - const quadrantWidth = - this.totalWidth - - (this.quadrantPadding * 2 + (showYAxis ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize: 0)); - const quadrantHeight = - this.totalHeight - - (this.quadrantPadding * 2 + (showXAxis ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize: 0)); - - const quadrantHalfWidth = quadrantWidth / 2; - const quadrantHalfHeight = quadrantHeight / 2; - - const axisLabels = []; - - if (this.xAxisLeftText && showXAxis) { - axisLabels.push({ - text: this.xAxisLeftText, - fill: this.xAxisTextFill, - x: quadrantLeft, - y: - this.xAxisPosition === 'top' - ? this.xAxisLabelPadding - : this.xAxisLabelPadding + quadrantTop + quadrantHeight, - fontSize: this.xAxisLabelFontSize, - verticalPos: 'left', - horizontalPos: 'top', - rotation: 0, - }); - } - if (this.xAxisRightText && showXAxis) { - axisLabels.push({ - text: this.xAxisRightText, - fill: this.xAxisTextFill, - x: quadrantLeft + quadrantHalfWidth, - y: - this.xAxisPosition === 'top' - ? this.xAxisLabelPadding - : this.xAxisLabelPadding + quadrantTop + quadrantHeight, - fontSize: this.xAxisLabelFontSize, - verticalPos: 'left', - horizontalPos: 'top', - rotation: 0, - }); - } - - if (this.yAxisBottomText && showYAxis) { - axisLabels.push({ - text: this.yAxisBottomText, - fill: this.yAxisTextFill, - x: - this.yAxisPosition === 'left' - ? this.yAxisLabelPadding - : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, - y: quadrantTop + quadrantHeight, - fontSize: this.yAxisLabelFontSize, - verticalPos: 'left', - horizontalPos: 'top', - rotation: -90, - }); - } - if (this.yAxisTopText && showYAxis) { - axisLabels.push({ - text: this.yAxisTopText, - fill: this.yAxisTextFill, - x: - this.yAxisPosition === 'left' - ? this.yAxisLabelPadding - : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, - y: quadrantTop + quadrantHalfHeight, - fontSize: this.yAxisLabelFontSize, - verticalPos: 'left', - horizontalPos: 'top', - rotation: -90, - }); - } - - const quadrants = [ - { - text: this.quadrant1Text, - textFill: this.quadrant1TextFill, - x: quadrantLeft + quadrantHalfWidth, - y: quadrantTop, - width: quadrantHalfWidth, - height: quadrantHalfHeight, - fill: this.quadrant1Fill, - }, - { - text: this.quadrant2Text, - textFill: this.quadrant2TextFill, - x: quadrantLeft, - y: quadrantTop, - width: quadrantHalfWidth, - height: quadrantHalfHeight, - fill: this.quadrant2Fill, - }, - { - text: this.quadrant3Text, - textFill: this.quadrant3TextFill, - x: quadrantLeft, - y: quadrantTop + quadrantHalfHeight, - width: quadrantHalfWidth, - height: quadrantHalfHeight, - fill: this.quadrant3Fill, - }, - { - text: this.quadrant4Text, - textFill: this.quadrant4TextFill, - x: quadrantLeft + quadrantHalfWidth, - y: quadrantTop + quadrantHalfHeight, - width: quadrantHalfWidth, - height: quadrantHalfHeight, - fill: this.quadrant4Fill, - }, - ]; - quadrants.forEach((quadrant, i) => { - // place the text in the center of the box - if (this.points.length === 0) { - quadrant.text = { - text: quadrant.text, - fill: quadrant.textFill, - x: quadrant.x + quadrant.width / 2, - y: quadrant.y + quadrant.height / 2, - fontSize: this.quadrantLabelFontSize, - verticalPos: 'center', - horizontalPos: 'center', - rotation: 0, - }; - // place the text top of the quadrant square - } else { - quadrant.text = { - text: quadrant.text, - fill: quadrant.textFill, - x: quadrant.x + quadrant.width / 2, - y: quadrant.y + this.quadrantTextTopPadding, - fontSize: this.quadrantLabelFontSize, - verticalPos: 'center', - horizontalPos: 'top', - rotation: 0, - }; - } - delete quadrant.textFill; - }); - - const xAxis = scaleLinear() - .domain([0, 1]) - .range([quadrantLeft, quadrantWidth + quadrantLeft]); - - const yAxis = scaleLinear() - .domain([0, 1]) - .range([quadrantHeight + quadrantTop, quadrantTop]); - - const points = this.points.map((point) => { - const props = { - x: xAxis(point.x), - y: yAxis(point.y), - fill: this.pointFill, - radius: this.pointRadius, - }; - props.text = { - text: point.text, - fill: this.pointTextFill, - x: props.x, - y: props.y + this.pointTextPadding, - verticalPos: 'center', - horizontalPos: 'top', - fontSize: this.pointLabelFontSize, - rotation: 0, - }; - return props; - }); - - return { - points, - quadrants, - axisLabels, - }; - } -} diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts new file mode 100644 index 000000000..4e0afb743 --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts @@ -0,0 +1,385 @@ +// @ts-ignore: TODO Fix ts errors +import { scaleLinear } from 'd3'; + +export type QuadrantPointInputType = { x: number; y: number; text: string }; + +export type TextVerticalPos = 'left' | 'center' | 'right'; +export type TextHorizontalPos = 'top' | 'middle' | 'bottom'; + +export type QuadrantTextType = { + text: string; + fill: string; + x: number; + y: number; + verticalPos: TextVerticalPos; + horizontalPos: TextHorizontalPos; + fontSize: number; + rotation: number; +}; + +export type QuadrantPointType = { + x: number; + y: number; + fill: string; + radius: number; + text: QuadrantTextType; +}; + +export type QuadrantQuadrantsType = { + text: QuadrantTextType; + x: number; + y: number; + width: number; + height: number; + fill: string; +}; + +export type QuadrantBuildType = { + points: QuadrantPointType[]; + quadrants: QuadrantQuadrantsType[]; + axisLabels: QuadrantTextType[]; +}; + +export class QuadrantBuilder { + private _quadrant1Text = ''; + private _quadrant2Text = ''; + private _quadrant3Text = ''; + private _quadrant4Text = ''; + private _xAxisLeftText = ''; + private _xAxisRightText = ''; + private _yAxisBottomText = ''; + private _yAxisTopText = ''; + private _totalHeight = 500; + private _totalWidth = 500; + public quadrantPadding = 5; + public xAxisLabelPadding = 5; + public yAxisLabelPadding = 5; + public xAxisLabelFontSize = 16; + public yAxisLabelFontSize = 16; + public quadrantLabelFontSize = 16; + public quadrantTextTopPadding = 5; + public pointTextPadding = 5; + public pointLabelFontSize = 12; + public pointRadius = 5; + public points: QuadrantPointInputType[] = []; + public xAxisPosition = 'top'; + public yAxisPosition = 'left'; + public quadrant1Fill = '#8bc2f3'; + public quadrant2Fill = '#faebd7'; + public quadrant3Fill = '#00ffff'; + public quadrant4Fill = '#f0ffff'; + public quadrant1TextFill = '#93690e'; + public quadrant2TextFill = '#8644ff'; + public quadrant3TextFill = '#e3004d'; + public quadrant4TextFill = '#000000'; + public pointFill = '#60B19C'; + public pointTextFill = '#0000ff'; + public xAxisTextFill = '#000000'; + public yAxisTextFill = '#000000'; + public showXAxis = true; + public showYAxis = true; + + clear() { + this.points = []; + this.quadrant1Text = ''; + this.quadrant2Text = ''; + this.quadrant3Text = ''; + this.quadrant4Text = ''; + this.xAxisLeftText = ''; + this.xAxisRightText = ''; + this.yAxisBottomText = ''; + this.yAxisTopText = ''; + } + + addPoints(points: QuadrantPointInputType[]) { + this.points = [...points, ...this.points]; + } + + set quadrant1Text(text: string) { + this._quadrant1Text = text; + } + + get quadrant1Text() { + return this._quadrant1Text; + } + + set quadrant2Text(text: string) { + this._quadrant2Text = text; + } + + get quadrant2Text() { + return this._quadrant2Text; + } + + set quadrant3Text(text: string) { + this._quadrant3Text = text; + } + + get quadrant3Text() { + return this._quadrant3Text; + } + + set quadrant4Text(text: string) { + this._quadrant4Text = text; + } + + get quadrant4Text() { + return this._quadrant4Text; + } + + set xAxisLeftText(text: string) { + this._xAxisLeftText = text; + } + + get xAxisLeftText() { + return this._xAxisLeftText; + } + + set xAxisRightText(text: string) { + this._xAxisRightText = text; + } + + get xAxisRightText() { + return this._xAxisRightText; + } + + set yAxisTopText(text: string) { + this._yAxisTopText = text; + } + + get yAxisTopText() { + return this._yAxisTopText; + } + + set yAxisBottomText(text: string) { + this._yAxisBottomText = text; + } + + get yAxisBottomText() { + return this._yAxisBottomText; + } + + set totalWidth(width: number) { + this._totalWidth = width; + } + + get totalWidth() { + return this._totalWidth; + } + + set totalHeight(height: number) { + this._totalHeight = height; + } + + get totalHeight() { + return this._totalHeight; + } + + build(): QuadrantBuildType { + const showXAxis = !this.xAxisLeftText && !this.xAxisRightText ? false : this.showXAxis; + const showYAxis = !this.yAxisTopText && !this.yAxisBottomText ? false : this.showYAxis; + const quadrantLeft = + this.quadrantPadding + + (this.yAxisPosition === 'left' && showYAxis + ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize + : 0); + const quadrantTop = + this.quadrantPadding + + (this.xAxisPosition === 'top' && showXAxis + ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize + : 0); + const quadrantWidth = + this.totalWidth - + (this.quadrantPadding * 2 + + (showYAxis ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize : 0)); + const quadrantHeight = + this.totalHeight - + (this.quadrantPadding * 2 + + (showXAxis ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize : 0)); + + const quadrantHalfWidth = quadrantWidth / 2; + const quadrantHalfHeight = quadrantHeight / 2; + + const axisLabels: QuadrantTextType[] = []; + + if (this.xAxisLeftText && showXAxis) { + axisLabels.push({ + text: this.xAxisLeftText, + fill: this.xAxisTextFill, + x: quadrantLeft, + y: + this.xAxisPosition === 'top' + ? this.xAxisLabelPadding + : this.xAxisLabelPadding + quadrantTop + quadrantHeight, + fontSize: this.xAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: 0, + }); + } + if (this.xAxisRightText && showXAxis) { + axisLabels.push({ + text: this.xAxisRightText, + fill: this.xAxisTextFill, + x: quadrantLeft + quadrantHalfWidth, + y: + this.xAxisPosition === 'top' + ? this.xAxisLabelPadding + : this.xAxisLabelPadding + quadrantTop + quadrantHeight, + fontSize: this.xAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: 0, + }); + } + + if (this.yAxisBottomText && showYAxis) { + axisLabels.push({ + text: this.yAxisBottomText, + fill: this.yAxisTextFill, + x: + this.yAxisPosition === 'left' + ? this.yAxisLabelPadding + : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, + y: quadrantTop + quadrantHeight, + fontSize: this.yAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: -90, + }); + } + if (this.yAxisTopText && showYAxis) { + axisLabels.push({ + text: this.yAxisTopText, + fill: this.yAxisTextFill, + x: + this.yAxisPosition === 'left' + ? this.yAxisLabelPadding + : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, + y: quadrantTop + quadrantHalfHeight, + fontSize: this.yAxisLabelFontSize, + verticalPos: 'left', + horizontalPos: 'top', + rotation: -90, + }); + } + + const quadrants: QuadrantQuadrantsType[] = [ + { + text: { + text: this.quadrant1Text, + fill: this.quadrant1TextFill, + x: 0, + y: 0, + fontSize: this.quadrantLabelFontSize, + verticalPos: 'center', + horizontalPos: 'middle', + rotation: 0, + }, + x: quadrantLeft + quadrantHalfWidth, + y: quadrantTop, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant1Fill, + }, + { + text: { + text: this.quadrant2Text, + fill: this.quadrant2TextFill, + x: 0, + y: 0, + fontSize: this.quadrantLabelFontSize, + verticalPos: 'center', + horizontalPos: 'middle', + rotation: 0, + }, + x: quadrantLeft, + y: quadrantTop, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant2Fill, + }, + { + text: { + text: this.quadrant3Text, + fill: this.quadrant3TextFill, + x: 0, + y: 0, + fontSize: this.quadrantLabelFontSize, + verticalPos: 'center', + horizontalPos: 'middle', + rotation: 0, + }, + x: quadrantLeft, + y: quadrantTop + quadrantHalfHeight, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant3Fill, + }, + { + text: { + text: this.quadrant4Text, + fill: this.quadrant4TextFill, + x: 0, + y: 0, + fontSize: this.quadrantLabelFontSize, + verticalPos: 'center', + horizontalPos: 'middle', + rotation: 0, + }, + x: quadrantLeft + quadrantHalfWidth, + y: quadrantTop + quadrantHalfHeight, + width: quadrantHalfWidth, + height: quadrantHalfHeight, + fill: this.quadrant4Fill, + }, + ]; + quadrants.forEach((quadrant, i) => { + // place the text in the center of the box + if (this.points.length === 0) { + quadrant.text.x = quadrant.x + quadrant.width / 2; + quadrant.text.y = quadrant.y + quadrant.height / 2; + quadrant.text.horizontalPos = 'middle'; + // place the text top of the quadrant square + } else { + quadrant.text.x = quadrant.x + quadrant.width / 2; + quadrant.text.y = quadrant.y + this.quadrantTextTopPadding; + quadrant.text.horizontalPos = 'top'; + } + }); + + const xAxis = scaleLinear() + .domain([0, 1]) + .range([quadrantLeft, quadrantWidth + quadrantLeft]); + + const yAxis = scaleLinear() + .domain([0, 1]) + .range([quadrantHeight + quadrantTop, quadrantTop]); + + const points: QuadrantPointType[] = this.points.map((point) => { + const props: QuadrantPointType = { + x: xAxis(point.x), + y: yAxis(point.y), + fill: this.pointFill, + radius: this.pointRadius, + text: { + text: point.text, + fill: this.pointTextFill, + x: xAxis(point.x), + y: yAxis(point.y) + this.pointTextPadding, + verticalPos: 'center', + horizontalPos: 'top', + fontSize: this.pointLabelFontSize, + rotation: 0, + }, + }; + return props; + }); + + return { + points, + quadrants, + axisLabels, + }; + } +} diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts similarity index 61% rename from packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js rename to packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts index a8d33a42d..630d6011c 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.js +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts @@ -15,54 +15,55 @@ import { QuadrantBuilder } from './quadrantBuilder.js'; const config = configApi.getConfig(); -function textSanitizer(text) { +function textSanitizer(text: string) { return sanitizeText(text.trim(), config); } +type LexTextObj = { text: string; type: 'text' | 'markdown' }; + const quadrantBuilder = new QuadrantBuilder(); -function setQuadrant1Text(textObj) { +function setQuadrant1Text(textObj: LexTextObj) { quadrantBuilder.quadrant1Text = textSanitizer(textObj.text); } -function setQuadrant2Text(textObj) { +function setQuadrant2Text(textObj: LexTextObj) { quadrantBuilder.quadrant2Text = textSanitizer(textObj.text); } -function setQuadrant3Text(textObj) { +function setQuadrant3Text(textObj: LexTextObj) { quadrantBuilder.quadrant3Text = textSanitizer(textObj.text); } -function setQuadrant4Text(textObj) { +function setQuadrant4Text(textObj: LexTextObj) { quadrantBuilder.quadrant4Text = textSanitizer(textObj.text); } -function setXAxisLeftText(textObj) { +function setXAxisLeftText(textObj: LexTextObj) { quadrantBuilder.xAxisLeftText = textSanitizer(textObj.text); } -function setXAxisRightText(textObj) { +function setXAxisRightText(textObj: LexTextObj) { quadrantBuilder.xAxisRightText = textSanitizer(textObj.text); } -function setYAxisTopText(textObj) { +function setYAxisTopText(textObj: LexTextObj) { quadrantBuilder.yAxisTopText = textSanitizer(textObj.text); } -function setYAxisBottomText(textObj) { +function setYAxisBottomText(textObj: LexTextObj) { quadrantBuilder.yAxisBottomText = textSanitizer(textObj.text); } -function addPoints(textObj, x, y) { - console.log(textObj, x, y); +function addPoints(textObj: LexTextObj, x: number, y: number) { quadrantBuilder.addPoints([{ x, y, text: textSanitizer(textObj.text) }]); } -function setWidth(width) { +function setWidth(width: number) { quadrantBuilder.totalWidth = width; } -function setHeight(height) { +function setHeight(height: number) { quadrantBuilder.totalHeight = height; } @@ -81,22 +82,25 @@ function getQuadrantData() { quadrantBuilder.pointTextFill = themeVariables.quadrantPointTextFill; quadrantBuilder.xAxisTextFill = themeVariables.quadrantXAxisTextFill; quadrantBuilder.yAxisTextFill = themeVariables.quadrantYAxisTextFill; - quadrantBuilder.quadrantPadding = quadrantChartConfig.quadrantPadding; - quadrantBuilder.xAxisLabelPadding = quadrantChartConfig.xAxisLabelPadding; - quadrantBuilder.yAxisLabelPadding = quadrantChartConfig.yAxisLabelPadding; - quadrantBuilder.xAxisLabelFontSize = quadrantChartConfig.xAxisLabelFontSize; - quadrantBuilder.yAxisLabelFontSize = quadrantChartConfig.yAxisLabelFontSize; - quadrantBuilder.quadrantLabelFontSize = quadrantChartConfig.quadrantLabelFontSize; - quadrantBuilder.quadrantTextTopPadding = quadrantChartConfig.quadrantTextTopPadding; - quadrantBuilder.pointTextPadding = quadrantChartConfig.pointTextPadding; - quadrantBuilder.pointLabelFontSize = quadrantChartConfig.pointLabelFontSize; - quadrantBuilder.pointRadius = quadrantChartConfig.pointRadius; - quadrantBuilder.xAxisPosition = quadrantChartConfig.xAxisPosition; - quadrantBuilder.yAxisPosition = quadrantChartConfig.yAxisPosition; + if (quadrantChartConfig) { + quadrantBuilder.quadrantPadding = quadrantChartConfig.quadrantPadding; + quadrantBuilder.xAxisLabelPadding = quadrantChartConfig.xAxisLabelPadding; + quadrantBuilder.yAxisLabelPadding = quadrantChartConfig.yAxisLabelPadding; + quadrantBuilder.xAxisLabelFontSize = quadrantChartConfig.xAxisLabelFontSize; + quadrantBuilder.yAxisLabelFontSize = quadrantChartConfig.yAxisLabelFontSize; + quadrantBuilder.quadrantLabelFontSize = quadrantChartConfig.quadrantLabelFontSize; + quadrantBuilder.quadrantTextTopPadding = quadrantChartConfig.quadrantTextTopPadding; + quadrantBuilder.pointTextPadding = quadrantChartConfig.pointTextPadding; + quadrantBuilder.pointLabelFontSize = quadrantChartConfig.pointLabelFontSize; + quadrantBuilder.pointRadius = quadrantChartConfig.pointRadius; + quadrantBuilder.xAxisPosition = quadrantChartConfig.xAxisPosition; + quadrantBuilder.yAxisPosition = quadrantChartConfig.yAxisPosition; + } return quadrantBuilder.build(); } -export const parseDirective = function (statement, context, type) { +export const parseDirective = function (statement: string, context: string, type: string) { + // @ts-ignore: TODO Fix ts errors mermaidAPI.parseDirective(this, statement, context, type); }; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts index ec3d6ed20..40ae798d2 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDiagram.ts @@ -2,12 +2,11 @@ import { DiagramDefinition } from '../../diagram-api/types.js'; // @ts-ignore: TODO Fix ts errors import parser from './parser/quadrant.jison'; import db from './quadrantDb.js'; -import styles from './styles.js'; import renderer from './quadrantRenderer.js'; export const diagram: DiagramDefinition = { parser, db, renderer, - styles, + styles: () => '', }; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js deleted file mode 100644 index 91936fb82..000000000 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.js +++ /dev/null @@ -1,131 +0,0 @@ -import { select, scaleLinear } from 'd3'; -import * as configApi from '../../config.js'; -import { log } from '../../logger.js'; - -import { configureSvgSize } from '../../setupGraphViewbox.js'; - -export const draw = (txt, id, _version, diagObj) => { - - - function getDominantBaseLine(horizintalPos) { - return horizintalPos === 'top' ? 'text-before-edge' : 'middle'; - } - - function getTextAnchor(verticalPos) { - return verticalPos === 'left' ? 'start' : 'middle'; - } - - function getTransformation(data) { - return `translate(${data.x}, ${data.y}) rotate(${data.rotation || 0})`; - } - - const conf = configApi.getConfig(); - log.debug('Rendering info diagram\n' + txt); - - 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'); - - const svg = root.select(`[id="${id}"]`); - - const group = svg.append('g').attr('class', 'main'); - - - // const bounds = svg.node().getBox(); - // const width = bounds.width + padding * 2; - // const height = bounds.height + padding * 2; - const width = 500; - const height = 500; - - diagObj.db.setHeight(height); - diagObj.db.setWidth(width); - - svg.attr('width', width); - svg.attr('height', height); - - const quadrantData = diagObj.db.getQuadrantData(); - - const quadrantsGroup = group.append('g').attr('class', 'quadrants'); - const dataPointGroup = group.append('g').attr('class', 'data-points'); - const labelGroup = group.append('g').attr('class', 'labels'); - - const quadrants = quadrantsGroup - .selectAll('g.quadrant') - .data(quadrantData.quadrants) - .enter() - .append('g') - .attr('class', 'quadrant'); - - quadrants - .append('rect') - .attr('x', data => data.x) - .attr('y', data => data.y) - .attr('width', data => data.width) - .attr('height', data => data.height) - .attr('fill', data => data.fill); - - quadrants - .append('text') - .attr('x', 0) - .attr('y', 0) - .attr('fill', data => data.text.fill) - .attr('font-size', data => data.text.fontSize) - .attr('dominant-baseline', data => getDominantBaseLine(data.text.horizontalPos)) - .attr('text-anchor', data => getTextAnchor(data.text.verticalPos)) - .attr('transform', data => getTransformation(data.text)) - .text(data => data.text.text); - - const labels = labelGroup - .selectAll('g.label') - .data(quadrantData.axisLabels) - .enter() - .append('g') - .attr('class', 'label') - - labels - .append('text') - .attr('x', 0) - .attr('y', 0) - .text(data => data.text) - .attr('fill', data => data.fill) - .attr('font-size', data => data.fontSize) - .attr('dominant-baseline', data => getDominantBaseLine(data.horizontalPos)) - .attr('text-anchor', data => getTextAnchor(data.verticalPos)) - .attr('transform', data => getTransformation(data)) - - const dataPoints = dataPointGroup - .selectAll('g.data-point') - .data(quadrantData.points) - .enter() - .append('g') - .attr('class', 'data-point') - - dataPoints - .append('circle') - .attr('cx', data => data.x) - .attr('cy', data => data.y) - .attr('r', data => data.radius) - .attr('fill', data => data.fill); - - dataPoints - .append('text') - .attr('x', 0) - .attr('y', 0) - .text(data => data.text.text) - .attr('fill', data => data.text.fill) - .attr('font-size', data => data.text.fontSize) - .attr('dominant-baseline', data => getDominantBaseLine(data.text.horizontalPos)) - .attr('text-anchor', data => getTextAnchor(data.text.verticalPos)) - .attr('transform', data => getTransformation(data.text)) -}; - -export default { - draw, -}; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts new file mode 100644 index 000000000..72639ba2e --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts @@ -0,0 +1,144 @@ +// @ts-ignore: TODO Fix ts errors +import { select } from 'd3'; +import * as configApi from '../../config.js'; +import { log } from '../../logger.js'; + +import { configureSvgSize } from '../../setupGraphViewbox.js'; +import { Diagram } from '../../Diagram.js'; +import { + QuadrantBuildType, + QuadrantPointType, + QuadrantQuadrantsType, + QuadrantTextType, + TextHorizontalPos, + TextVerticalPos, +} from './quadrantBuilder.js'; + +export const draw = (txt: string, id: string, _version: string, diagObj: Diagram) => { + function getDominantBaseLine(horizintalPos: TextHorizontalPos) { + return horizintalPos === 'top' ? 'text-before-edge' : 'middle'; + } + + function getTextAnchor(verticalPos: TextVerticalPos) { + return verticalPos === 'left' ? 'start' : 'middle'; + } + + function getTransformation(data: { x: number; y: number; rotation: number }) { + return `translate(${data.x}, ${data.y}) rotate(${data.rotation || 0})`; + } + + const conf = configApi.getConfig(); + + log.debug('Rendering info diagram\n' + txt); + + 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'); + + const svg = root.select(`[id="${id}"]`); + + const group = svg.append('g').attr('class', 'main'); + + const width = conf.quadrantChart?.chartWidth || 500; + const height = conf.quadrantChart?.chartHeight || 500; + + configureSvgSize(svg, height, width, conf.quadrantChart?.useMaxWidth || true); + + svg.attr('viewBox', '0 0 ' + width + ' ' + height); + + // @ts-ignore: TODO Fix ts errors + diagObj.db.setHeight(height); + // @ts-ignore: TODO Fix ts errors + diagObj.db.setWidth(width); + + // @ts-ignore: TODO Fix ts errors + const quadrantData: QuadrantBuildType = diagObj.db.getQuadrantData(); + + const quadrantsGroup = group.append('g').attr('class', 'quadrants'); + const dataPointGroup = group.append('g').attr('class', 'data-points'); + const labelGroup = group.append('g').attr('class', 'labels'); + + const quadrants = quadrantsGroup + .selectAll('g.quadrant') + .data(quadrantData.quadrants) + .enter() + .append('g') + .attr('class', 'quadrant'); + + quadrants + .append('rect') + .attr('x', (data: QuadrantQuadrantsType) => data.x) + .attr('y', (data: QuadrantQuadrantsType) => data.y) + .attr('width', (data: QuadrantQuadrantsType) => data.width) + .attr('height', (data: QuadrantQuadrantsType) => data.height) + .attr('fill', (data: QuadrantQuadrantsType) => data.fill); + + quadrants + .append('text') + .attr('x', 0) + .attr('y', 0) + .attr('fill', (data: QuadrantQuadrantsType) => data.text.fill) + .attr('font-size', (data: QuadrantQuadrantsType) => data.text.fontSize) + .attr('dominant-baseline', (data: QuadrantQuadrantsType) => + getDominantBaseLine(data.text.horizontalPos) + ) + .attr('text-anchor', (data: QuadrantQuadrantsType) => getTextAnchor(data.text.verticalPos)) + .attr('transform', (data: QuadrantQuadrantsType) => getTransformation(data.text)) + .text((data: QuadrantQuadrantsType) => data.text.text); + + const labels = labelGroup + .selectAll('g.label') + .data(quadrantData.axisLabels) + .enter() + .append('g') + .attr('class', 'label'); + + labels + .append('text') + .attr('x', 0) + .attr('y', 0) + .text((data: QuadrantTextType) => data.text) + .attr('fill', (data: QuadrantTextType) => data.fill) + .attr('font-size', (data: QuadrantTextType) => data.fontSize) + .attr('dominant-baseline', (data: QuadrantTextType) => getDominantBaseLine(data.horizontalPos)) + .attr('text-anchor', (data: QuadrantTextType) => getTextAnchor(data.verticalPos)) + .attr('transform', (data: QuadrantTextType) => getTransformation(data)); + + const dataPoints = dataPointGroup + .selectAll('g.data-point') + .data(quadrantData.points) + .enter() + .append('g') + .attr('class', 'data-point'); + + dataPoints + .append('circle') + .attr('cx', (data: QuadrantPointType) => data.x) + .attr('cy', (data: QuadrantPointType) => data.y) + .attr('r', (data: QuadrantPointType) => data.radius) + .attr('fill', (data: QuadrantPointType) => data.fill); + + dataPoints + .append('text') + .attr('x', 0) + .attr('y', 0) + .text((data: QuadrantPointType) => data.text.text) + .attr('fill', (data: QuadrantPointType) => data.text.fill) + .attr('font-size', (data: QuadrantPointType) => data.text.fontSize) + .attr('dominant-baseline', (data: QuadrantPointType) => + getDominantBaseLine(data.text.horizontalPos) + ) + .attr('text-anchor', (data: QuadrantPointType) => getTextAnchor(data.text.verticalPos)) + .attr('transform', (data: QuadrantPointType) => getTransformation(data.text)); +}; + +export default { + draw, +}; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/styles.js b/packages/mermaid/src/diagrams/quadrant-chart/styles.js deleted file mode 100644 index fc7ab4bfd..000000000 --- a/packages/mermaid/src/diagrams/quadrant-chart/styles.js +++ /dev/null @@ -1,5 +0,0 @@ -const getStyles = (options) => -` -`; - -export default getStyles; diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index 30bbfd5b6..53c5f73b6 100644 --- a/packages/mermaid/src/themes/theme-base.js +++ b/packages/mermaid/src/themes/theme-base.js @@ -1,4 +1,4 @@ -import { darken, lighten, adjust, invert } from 'khroma'; +import { darken, lighten, adjust, invert, isDark } from 'khroma'; import { mkBorder } from './theme-helpers.js'; import { oldAttributeBackgroundColorEven, @@ -222,15 +222,21 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; - this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); - this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); - this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); - this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; - this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrant2TextFill = + this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant3TextFill = + this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant4TextFill = + this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrantPointFill = + this.quadrantPointFill || isDark(this.quadrant1Fill) + ? lighten(this.quadrant1Fill) + : darken(this.quadrant1Fill); + this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; diff --git a/packages/mermaid/src/themes/theme-dark.js b/packages/mermaid/src/themes/theme-dark.js index 41c624588..d1f5dbde6 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -1,4 +1,4 @@ -import { invert, lighten, darken, rgba, adjust } from 'khroma'; +import { invert, lighten, darken, rgba, adjust, isDark } from 'khroma'; import { mkBorder } from './theme-helpers.js'; class Theme { @@ -228,15 +228,21 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; - this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); - this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); - this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); - this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; - this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrant2TextFill = + this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant3TextFill = + this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant4TextFill = + this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrantPointFill = + this.quadrantPointFill || isDark(this.quadrant1Fill) + ? lighten(this.quadrant1Fill) + : darken(this.quadrant1Fill); + this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index 091240af0..f73a05c4d 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -1,4 +1,4 @@ -import { invert, lighten, rgba, adjust, darken } from 'khroma'; +import { invert, lighten, rgba, adjust, darken, isDark } from 'khroma'; import { mkBorder } from './theme-helpers.js'; import { oldAttributeBackgroundColorEven, @@ -249,15 +249,21 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; - this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); - this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); - this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); - this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; - this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrant2TextFill = + this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant3TextFill = + this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant4TextFill = + this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrantPointFill = + this.quadrantPointFill || isDark(this.quadrant1Fill) + ? lighten(this.quadrant1Fill) + : darken(this.quadrant1Fill); + this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; diff --git a/packages/mermaid/src/themes/theme-forest.js b/packages/mermaid/src/themes/theme-forest.js index 0c4af6ec3..f3e41adec 100644 --- a/packages/mermaid/src/themes/theme-forest.js +++ b/packages/mermaid/src/themes/theme-forest.js @@ -1,4 +1,4 @@ -import { darken, lighten, adjust, invert } from 'khroma'; +import { darken, lighten, adjust, invert, isDark } from 'khroma'; import { mkBorder } from './theme-helpers.js'; import { oldAttributeBackgroundColorEven, @@ -217,15 +217,21 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; - this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); - this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); - this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); - this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; - this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrant2TextFill = + this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant3TextFill = + this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant4TextFill = + this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrantPointFill = + this.quadrantPointFill || isDark(this.quadrant1Fill) + ? lighten(this.quadrant1Fill) + : darken(this.quadrant1Fill); + this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index 1f069c4fd..f240404a7 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -1,4 +1,4 @@ -import { invert, darken, lighten, adjust } from 'khroma'; +import { invert, darken, lighten, adjust, isDark } from 'khroma'; import { mkBorder } from './theme-helpers.js'; import { oldAttributeBackgroundColorEven, @@ -248,15 +248,21 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, {r: 5, g: 5, b: 5}); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, {r: 10, g: 10, b: 10}); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, {r: 15, g: 15, b: 15}); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; - this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.quadrant1TextFill, {r: -5, g: -5, b: -5}); - this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.quadrant1TextFill, {r: -10, g: -10, b: -10}); - this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.quadrant1TextFill, {r: -15, g: -15, b: -15}); - this.quadrantPointFill = this.quadrantPointFill || this.secondaryColor; - this.quadrantPointTextFill = this.quadrantPointTextFill || this.secondaryTextColor; + this.quadrant2TextFill = + this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant3TextFill = + this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant4TextFill = + this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrantPointFill = + this.quadrantPointFill || isDark(this.quadrant1Fill) + ? lighten(this.quadrant1Fill) + : darken(this.quadrant1Fill); + this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; From 4cbcfa054e1b0a878e5bb14cd541841246a4e0ff Mon Sep 17 00:00:00 2001 From: amsubhash <amsubhash@users.noreply.github.com> Date: Tue, 9 May 2023 14:50:54 +0000 Subject: [PATCH 3/9] Update docs --- docs/config/setup/modules/defaultConfig.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index ad8f90248..284acf6b2 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[defaultConfig.ts:2115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2115) +[defaultConfig.ts:2257](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2257) --- From 125373396232f681a828aea5a14eb0fc2b2b5758 Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Thu, 11 May 2023 12:10:58 +0530 Subject: [PATCH 4/9] Fixed review comment --- demos/quadrantchart.html | 7 +- docs/config/setup/modules/defaultConfig.md | 2 +- packages/mermaid/src/config.type.ts | 4 + packages/mermaid/src/defaultConfig.ts | 36 ++ .../quadrant-chart/parser/quadrant.jison | 6 +- .../quadrant-chart/quadrantBuilder.ts | 525 +++++++++++------- .../src/diagrams/quadrant-chart/quadrantDb.ts | 67 ++- .../quadrant-chart/quadrantRenderer.ts | 32 +- packages/mermaid/src/themes/theme-base.js | 5 + packages/mermaid/src/themes/theme-dark.js | 5 + packages/mermaid/src/themes/theme-default.js | 5 + packages/mermaid/src/themes/theme-forest.js | 5 + packages/mermaid/src/themes/theme-neutral.js | 5 + 13 files changed, 446 insertions(+), 258 deletions(-) diff --git a/demos/quadrantchart.html b/demos/quadrantchart.html index 63a279bba..a2a60d0a1 100644 --- a/demos/quadrantchart.html +++ b/demos/quadrantchart.html @@ -27,9 +27,10 @@ </pre> <pre class="mermaid"> - %%{init: {"quadrantChart": {"xAxisPosition": "bottom", "chartWidth": 600, "chartHeight": 600} } }%% + %%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% quadrantChart - x-axis "Completeness of Vision ❤" + title Analytics and Business Intelligence Platforms + x-axis "Completeness of Vision ❤" --> y-axis Ability to Execute quadrant-1 Leaders quadrant-2 Challengers @@ -46,7 +47,7 @@ import mermaid from './mermaid.esm.mjs'; mermaid.initialize({ theme: 'default', - logLevel: 1, + logLevel: 3, securityLevel: 'loose', }); </script> diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index 284acf6b2..d95ec4e92 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[defaultConfig.ts:2257](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2257) +[defaultConfig.ts:2293](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2293) --- diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 3820f2c30..140c200fb 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -230,6 +230,8 @@ export interface PieDiagramConfig extends BaseDiagramConfig { export interface QuadrantChartConfig extends BaseDiagramConfig { chartWidth: number; chartHeight: number; + titleFontSize: number; + titlePadding: number; quadrantPadding: number; xAxisLabelPadding: number; yAxisLabelPadding: number; @@ -242,6 +244,8 @@ export interface QuadrantChartConfig extends BaseDiagramConfig { pointRadius: number; xAxisPosition: 'top' | 'bottom'; yAxisPosition: 'left' | 'right'; + quadrantInternalBorderStrokeWidth: number; + quadrantExternalBorderStrokeWidth: number; } export interface ErDiagramConfig extends BaseDiagramConfig { diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index ec3f55cb5..ade873e57 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -1326,6 +1326,24 @@ const config: Partial<MermaidConfig> = { * Default value: 5 */ yAxisLabelPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | titlePadding | Chart title top and bottom padding | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + titlePadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | titleFontSize | Chart title font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 20 + */ + titleFontSize: 20, /** * | Parameter | Description | Type | Required | Values | * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | @@ -1407,6 +1425,24 @@ const config: Partial<MermaidConfig> = { * Default value: left */ yAxisPosition: 'left', + /** + * | Parameter | Description | Type | Required | Values | + * | --------------------------------- | ------------------------------------------------------------- | ------- | -------- | ------------------- | + * | quadrantInternalBorderStrokeWidth | stroke width of edges of the box that are inside the quadrant | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 1 + */ + quadrantInternalBorderStrokeWidth: 1, + /** + * | Parameter | Description | Type | Required | Values | + * | --------------------------------- | -------------------------------------------------------------- | ------- | -------- | ------------------- | + * | quadrantExternalBorderStrokeWidth | stroke width of edges of the box that are outside the quadrant | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 2 + */ + quadrantExternalBorderStrokeWidth: 2, /** * | Parameter | Description | Type | Required | Values | * | ----------- | ----------- | ------- | -------- | ----------- | diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison index ee8cf2ab2..67c47cb13 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison @@ -44,7 +44,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} \s*"x-axis"\s* return 'X-AXIS'; \s*"y-axis"\s* return 'Y-AXIS'; -\s*\-\-\>\s* return 'AXIS-TEXT-DELIMITER' +\s*\-\-+\>[^(\r?\n)\s]* return 'AXIS-TEXT-DELIMITER' \s*"quadrant-1"\s* return 'QUADRANT_1'; \s*"quadrant-2"\s* return 'QUADRANT_2'; \s*"quadrant-3"\s* return 'QUADRANT_3'; @@ -118,12 +118,14 @@ statement ; points - : text point_start point_x point_y {yy.addPoints($1, $3, $4);}; + : text point_start point_x point_y {yy.addPoint($1, $3, $4);}; axisDetails : X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);} + | X-AXIS text AXIS-TEXT-DELIMITER {$2.text += $3; yy.setXAxisLeftText($2);} | X-AXIS text {yy.setXAxisLeftText($2);} | Y-AXIS text AXIS-TEXT-DELIMITER text {yy.setYAxisBottomText($2); yy.setYAxisTopText($4);} + | Y-AXIS text AXIS-TEXT-DELIMITER {$2.text += $3; yy.setYAxisBottomText($2);} | Y-AXIS text {yy.setYAxisBottomText($2);} ; diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts index 4e0afb743..231af941b 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts @@ -1,12 +1,18 @@ // @ts-ignore: TODO Fix ts errors import { scaleLinear } from 'd3'; +import { log } from '../../logger.js'; +import { QuadrantChartConfig } from '../../config.type.js'; -export type QuadrantPointInputType = { x: number; y: number; text: string }; +export interface QuadrantPointInputType { + x: number; + y: number; + text: string; +} export type TextVerticalPos = 'left' | 'center' | 'right'; export type TextHorizontalPos = 'top' | 'middle' | 'bottom'; -export type QuadrantTextType = { +export interface QuadrantTextType { text: string; fill: string; x: number; @@ -15,250 +21,267 @@ export type QuadrantTextType = { horizontalPos: TextHorizontalPos; fontSize: number; rotation: number; -}; +} -export type QuadrantPointType = { +export interface QuadrantPointType { x: number; y: number; fill: string; radius: number; text: QuadrantTextType; -}; +} -export type QuadrantQuadrantsType = { +export interface QuadrantLineType { + strokeWidth: number; + strokeFill: string; + x1: number; + y1: number; + x2: number; + y2: number; +} + +export interface QuadrantQuadrantsType { text: QuadrantTextType; x: number; y: number; width: number; height: number; fill: string; -}; +} -export type QuadrantBuildType = { +export interface QuadrantBuildType { points: QuadrantPointType[]; quadrants: QuadrantQuadrantsType[]; axisLabels: QuadrantTextType[]; -}; + title?: QuadrantTextType; + borderLines?: QuadrantLineType[]; +} + +export interface QuadrantBuilderConfig extends QuadrantChartConfig { + titleText: string; + quadrant1Text: string; + quadrant2Text: string; + quadrant3Text: string; + quadrant4Text: string; + xAxisLeftText: string; + xAxisRightText: string; + yAxisBottomText: string; + yAxisTopText: string; + points: QuadrantPointInputType[]; + showXAxis: boolean; + showYAxis: boolean; + showTitle: boolean; +} + +export interface QuadrantBuilderThemeConfig { + quadrantTitleFill: string; + quadrant1Fill: string; + quadrant2Fill: string; + quadrant3Fill: string; + quadrant4Fill: string; + quadrant1TextFill: string; + quadrant2TextFill: string; + quadrant3TextFill: string; + quadrant4TextFill: string; + quadrantPointFill: string; + quadrantPointTextFill: string; + quadrantXAxisTextFill: string; + quadrantYAxisTextFill: string; + quadrantInternalBorderStrokeFill: string; + quadrantExternalBorderStrokeFill: string; +} export class QuadrantBuilder { - private _quadrant1Text = ''; - private _quadrant2Text = ''; - private _quadrant3Text = ''; - private _quadrant4Text = ''; - private _xAxisLeftText = ''; - private _xAxisRightText = ''; - private _yAxisBottomText = ''; - private _yAxisTopText = ''; - private _totalHeight = 500; - private _totalWidth = 500; - public quadrantPadding = 5; - public xAxisLabelPadding = 5; - public yAxisLabelPadding = 5; - public xAxisLabelFontSize = 16; - public yAxisLabelFontSize = 16; - public quadrantLabelFontSize = 16; - public quadrantTextTopPadding = 5; - public pointTextPadding = 5; - public pointLabelFontSize = 12; - public pointRadius = 5; - public points: QuadrantPointInputType[] = []; - public xAxisPosition = 'top'; - public yAxisPosition = 'left'; - public quadrant1Fill = '#8bc2f3'; - public quadrant2Fill = '#faebd7'; - public quadrant3Fill = '#00ffff'; - public quadrant4Fill = '#f0ffff'; - public quadrant1TextFill = '#93690e'; - public quadrant2TextFill = '#8644ff'; - public quadrant3TextFill = '#e3004d'; - public quadrant4TextFill = '#000000'; - public pointFill = '#60B19C'; - public pointTextFill = '#0000ff'; - public xAxisTextFill = '#000000'; - public yAxisTextFill = '#000000'; - public showXAxis = true; - public showYAxis = true; + private config: QuadrantBuilderConfig; + private themeConfig: QuadrantBuilderThemeConfig; + + constructor() { + this.config = this.getDefaultConfig(); + this.themeConfig = this.getDefaultThemeConfig(); + } + + getDefaultConfig(): QuadrantBuilderConfig { + return { + titleText: '', + quadrant1Text: '', + quadrant2Text: '', + quadrant3Text: '', + quadrant4Text: '', + xAxisLeftText: '', + xAxisRightText: '', + yAxisBottomText: '', + yAxisTopText: '', + points: [], + showXAxis: true, + showYAxis: true, + showTitle: true, + chartHeight: 500, + chartWidth: 500, + titlePadding: 5, + titleFontSize: 20, + quadrantPadding: 5, + xAxisLabelPadding: 5, + yAxisLabelPadding: 5, + xAxisLabelFontSize: 16, + yAxisLabelFontSize: 16, + quadrantLabelFontSize: 16, + quadrantTextTopPadding: 5, + pointTextPadding: 5, + pointLabelFontSize: 12, + pointRadius: 5, + xAxisPosition: 'top', + yAxisPosition: 'left', + quadrantInternalBorderStrokeWidth: 2, + quadrantExternalBorderStrokeWidth: 3, + }; + } + + getDefaultThemeConfig(): QuadrantBuilderThemeConfig { + return { + quadrant1Fill: '#8bc2f3', + quadrant2Fill: '#faebd7', + quadrant3Fill: '#00ffff', + quadrant4Fill: '#f0ffff', + quadrant1TextFill: '#93690e', + quadrant2TextFill: '#8644ff', + quadrant3TextFill: '#e3004d', + quadrant4TextFill: '#000000', + quadrantPointFill: '#60B19C', + quadrantPointTextFill: '#0000ff', + quadrantXAxisTextFill: '#000000', + quadrantYAxisTextFill: '#000000', + quadrantTitleFill: '#000000', + quadrantInternalBorderStrokeFill: '#000000', + quadrantExternalBorderStrokeFill: '#000000', + }; + } clear() { - this.points = []; - this.quadrant1Text = ''; - this.quadrant2Text = ''; - this.quadrant3Text = ''; - this.quadrant4Text = ''; - this.xAxisLeftText = ''; - this.xAxisRightText = ''; - this.yAxisBottomText = ''; - this.yAxisTopText = ''; + this.config = this.getDefaultConfig(); + this.themeConfig = this.getDefaultThemeConfig(); + log.info('clear called'); } addPoints(points: QuadrantPointInputType[]) { - this.points = [...points, ...this.points]; + this.config.points = [...points, ...this.config.points]; } - set quadrant1Text(text: string) { - this._quadrant1Text = text; + setConfig(config: Partial<QuadrantBuilderConfig>) { + log.trace('setConfig called with: ', config); + this.config = { ...this.config, ...config }; } - get quadrant1Text() { - return this._quadrant1Text; - } - - set quadrant2Text(text: string) { - this._quadrant2Text = text; - } - - get quadrant2Text() { - return this._quadrant2Text; - } - - set quadrant3Text(text: string) { - this._quadrant3Text = text; - } - - get quadrant3Text() { - return this._quadrant3Text; - } - - set quadrant4Text(text: string) { - this._quadrant4Text = text; - } - - get quadrant4Text() { - return this._quadrant4Text; - } - - set xAxisLeftText(text: string) { - this._xAxisLeftText = text; - } - - get xAxisLeftText() { - return this._xAxisLeftText; - } - - set xAxisRightText(text: string) { - this._xAxisRightText = text; - } - - get xAxisRightText() { - return this._xAxisRightText; - } - - set yAxisTopText(text: string) { - this._yAxisTopText = text; - } - - get yAxisTopText() { - return this._yAxisTopText; - } - - set yAxisBottomText(text: string) { - this._yAxisBottomText = text; - } - - get yAxisBottomText() { - return this._yAxisBottomText; - } - - set totalWidth(width: number) { - this._totalWidth = width; - } - - get totalWidth() { - return this._totalWidth; - } - - set totalHeight(height: number) { - this._totalHeight = height; - } - - get totalHeight() { - return this._totalHeight; + setThemeConfig(themeConfig: Partial<QuadrantBuilderThemeConfig>) { + log.trace('setThemeConfig called with: ', themeConfig); + this.themeConfig = { ...this.themeConfig, ...themeConfig }; } build(): QuadrantBuildType { - const showXAxis = !this.xAxisLeftText && !this.xAxisRightText ? false : this.showXAxis; - const showYAxis = !this.yAxisTopText && !this.yAxisBottomText ? false : this.showYAxis; - const quadrantLeft = - this.quadrantPadding + - (this.yAxisPosition === 'left' && showYAxis - ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize - : 0); - const quadrantTop = - this.quadrantPadding + - (this.xAxisPosition === 'top' && showXAxis - ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize - : 0); + const showXAxis = + this.config.showXAxis && (this.config.xAxisLeftText || this.config.xAxisRightText); + const showYAxis = + this.config.showYAxis && (this.config.yAxisTopText || this.config.yAxisBottomText); + const showTitle = this.config.showTitle && this.config.titleText; + + const halfExternalBorderWidth = this.config.quadrantExternalBorderStrokeWidth / 2; + const halfInternalBorderWidth = this.config.quadrantInternalBorderStrokeWidth / 2; + + const xAxisPosition = this.config.points.length > 0 ? 'bottom' : this.config.xAxisPosition; + + const drawAxisLabelInMiddle = this.config.points.length === 0; + + const xAxisSpaceCalculation = + this.config.xAxisLabelPadding * 2 + this.config.xAxisLabelFontSize; + const xAxisSpace = { + top: xAxisPosition === 'top' && showXAxis ? xAxisSpaceCalculation : 0, + bottom: xAxisPosition === 'bottom' && showXAxis ? xAxisSpaceCalculation : 0, + }; + + const yAxisSpaceCalculation = + this.config.yAxisLabelPadding * 2 + this.config.yAxisLabelFontSize; + const yAxisSpace = { + left: this.config.yAxisPosition === 'left' && showYAxis ? yAxisSpaceCalculation : 0, + right: this.config.yAxisPosition === 'right' && showYAxis ? yAxisSpaceCalculation : 0, + }; + + const titleSpaceCalculation = this.config.titleFontSize + this.config.titlePadding * 2; + const titleSpace = { + top: showTitle ? titleSpaceCalculation : 0, + }; + + const quadrantLeft = this.config.quadrantPadding + yAxisSpace.left; + const quadrantTop = this.config.quadrantPadding + xAxisSpace.top + titleSpace.top; const quadrantWidth = - this.totalWidth - - (this.quadrantPadding * 2 + - (showYAxis ? this.yAxisLabelPadding * 2 + this.yAxisLabelFontSize : 0)); + this.config.chartWidth - this.config.quadrantPadding * 2 - yAxisSpace.left - yAxisSpace.right; const quadrantHeight = - this.totalHeight - - (this.quadrantPadding * 2 + - (showXAxis ? this.xAxisLabelPadding * 2 + this.xAxisLabelFontSize : 0)); + this.config.chartHeight - + this.config.quadrantPadding * 2 - + xAxisSpace.top - + xAxisSpace.bottom - + titleSpace.top; const quadrantHalfWidth = quadrantWidth / 2; const quadrantHalfHeight = quadrantHeight / 2; const axisLabels: QuadrantTextType[] = []; - if (this.xAxisLeftText && showXAxis) { + if (this.config.xAxisLeftText && showXAxis) { axisLabels.push({ - text: this.xAxisLeftText, - fill: this.xAxisTextFill, - x: quadrantLeft, + text: this.config.xAxisLeftText, + fill: this.themeConfig.quadrantXAxisTextFill, + x: quadrantLeft + (drawAxisLabelInMiddle ? quadrantHalfWidth / 2 : 0), y: - this.xAxisPosition === 'top' - ? this.xAxisLabelPadding - : this.xAxisLabelPadding + quadrantTop + quadrantHeight, - fontSize: this.xAxisLabelFontSize, - verticalPos: 'left', + xAxisPosition === 'top' + ? this.config.xAxisLabelPadding + titleSpace.top + : this.config.xAxisLabelPadding + quadrantTop + quadrantHeight, + fontSize: this.config.xAxisLabelFontSize, + verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', horizontalPos: 'top', rotation: 0, }); } - if (this.xAxisRightText && showXAxis) { + if (this.config.xAxisRightText && showXAxis) { axisLabels.push({ - text: this.xAxisRightText, - fill: this.xAxisTextFill, - x: quadrantLeft + quadrantHalfWidth, + text: this.config.xAxisRightText, + fill: this.themeConfig.quadrantXAxisTextFill, + x: quadrantLeft + quadrantHalfWidth + (drawAxisLabelInMiddle ? quadrantHalfWidth / 2 : 0), y: - this.xAxisPosition === 'top' - ? this.xAxisLabelPadding - : this.xAxisLabelPadding + quadrantTop + quadrantHeight, - fontSize: this.xAxisLabelFontSize, - verticalPos: 'left', + xAxisPosition === 'top' + ? this.config.xAxisLabelPadding + titleSpace.top + : this.config.xAxisLabelPadding + quadrantTop + quadrantHeight, + fontSize: this.config.xAxisLabelFontSize, + verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', horizontalPos: 'top', rotation: 0, }); } - if (this.yAxisBottomText && showYAxis) { + if (this.config.yAxisBottomText && showYAxis) { axisLabels.push({ - text: this.yAxisBottomText, - fill: this.yAxisTextFill, + text: this.config.yAxisBottomText, + fill: this.themeConfig.quadrantYAxisTextFill, x: - this.yAxisPosition === 'left' - ? this.yAxisLabelPadding - : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, - y: quadrantTop + quadrantHeight, - fontSize: this.yAxisLabelFontSize, - verticalPos: 'left', + this.config.yAxisPosition === 'left' + ? this.config.yAxisLabelPadding + : this.config.yAxisLabelPadding + quadrantLeft + quadrantWidth, + y: quadrantTop + quadrantHeight - (drawAxisLabelInMiddle ? quadrantHalfHeight / 2 : 0), + fontSize: this.config.yAxisLabelFontSize, + verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', horizontalPos: 'top', rotation: -90, }); } - if (this.yAxisTopText && showYAxis) { + if (this.config.yAxisTopText && showYAxis) { axisLabels.push({ - text: this.yAxisTopText, - fill: this.yAxisTextFill, + text: this.config.yAxisTopText, + fill: this.themeConfig.quadrantYAxisTextFill, x: - this.yAxisPosition === 'left' - ? this.yAxisLabelPadding - : this.yAxisLabelPadding + quadrantLeft + quadrantWidth, - y: quadrantTop + quadrantHalfHeight, - fontSize: this.yAxisLabelFontSize, - verticalPos: 'left', + this.config.yAxisPosition === 'left' + ? this.config.yAxisLabelPadding + : this.config.yAxisLabelPadding + quadrantLeft + quadrantWidth, + y: quadrantTop + quadrantHalfHeight - (drawAxisLabelInMiddle ? quadrantHalfHeight / 2 : 0), + fontSize: this.config.yAxisLabelFontSize, + verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', horizontalPos: 'top', rotation: -90, }); @@ -267,11 +290,11 @@ export class QuadrantBuilder { const quadrants: QuadrantQuadrantsType[] = [ { text: { - text: this.quadrant1Text, - fill: this.quadrant1TextFill, + text: this.config.quadrant1Text, + fill: this.themeConfig.quadrant1TextFill, x: 0, y: 0, - fontSize: this.quadrantLabelFontSize, + fontSize: this.config.quadrantLabelFontSize, verticalPos: 'center', horizontalPos: 'middle', rotation: 0, @@ -280,15 +303,15 @@ export class QuadrantBuilder { y: quadrantTop, width: quadrantHalfWidth, height: quadrantHalfHeight, - fill: this.quadrant1Fill, + fill: this.themeConfig.quadrant1Fill, }, { text: { - text: this.quadrant2Text, - fill: this.quadrant2TextFill, + text: this.config.quadrant2Text, + fill: this.themeConfig.quadrant2TextFill, x: 0, y: 0, - fontSize: this.quadrantLabelFontSize, + fontSize: this.config.quadrantLabelFontSize, verticalPos: 'center', horizontalPos: 'middle', rotation: 0, @@ -297,15 +320,15 @@ export class QuadrantBuilder { y: quadrantTop, width: quadrantHalfWidth, height: quadrantHalfHeight, - fill: this.quadrant2Fill, + fill: this.themeConfig.quadrant2Fill, }, { text: { - text: this.quadrant3Text, - fill: this.quadrant3TextFill, + text: this.config.quadrant3Text, + fill: this.themeConfig.quadrant3TextFill, x: 0, y: 0, - fontSize: this.quadrantLabelFontSize, + fontSize: this.config.quadrantLabelFontSize, verticalPos: 'center', horizontalPos: 'middle', rotation: 0, @@ -314,15 +337,15 @@ export class QuadrantBuilder { y: quadrantTop + quadrantHalfHeight, width: quadrantHalfWidth, height: quadrantHalfHeight, - fill: this.quadrant3Fill, + fill: this.themeConfig.quadrant3Fill, }, { text: { - text: this.quadrant4Text, - fill: this.quadrant4TextFill, + text: this.config.quadrant4Text, + fill: this.themeConfig.quadrant4TextFill, x: 0, y: 0, - fontSize: this.quadrantLabelFontSize, + fontSize: this.config.quadrantLabelFontSize, verticalPos: 'center', horizontalPos: 'middle', rotation: 0, @@ -331,22 +354,21 @@ export class QuadrantBuilder { y: quadrantTop + quadrantHalfHeight, width: quadrantHalfWidth, height: quadrantHalfHeight, - fill: this.quadrant4Fill, + fill: this.themeConfig.quadrant4Fill, }, ]; - quadrants.forEach((quadrant, i) => { + for (const quadrant of quadrants) { + quadrant.text.x = quadrant.x + quadrant.width / 2; // place the text in the center of the box - if (this.points.length === 0) { - quadrant.text.x = quadrant.x + quadrant.width / 2; + if (this.config.points.length === 0) { quadrant.text.y = quadrant.y + quadrant.height / 2; quadrant.text.horizontalPos = 'middle'; // place the text top of the quadrant square } else { - quadrant.text.x = quadrant.x + quadrant.width / 2; - quadrant.text.y = quadrant.y + this.quadrantTextTopPadding; + quadrant.text.y = quadrant.y + this.config.quadrantTextTopPadding; quadrant.text.horizontalPos = 'top'; } - }); + } const xAxis = scaleLinear() .domain([0, 1]) @@ -356,30 +378,103 @@ export class QuadrantBuilder { .domain([0, 1]) .range([quadrantHeight + quadrantTop, quadrantTop]); - const points: QuadrantPointType[] = this.points.map((point) => { + const points: QuadrantPointType[] = this.config.points.map((point) => { const props: QuadrantPointType = { x: xAxis(point.x), y: yAxis(point.y), - fill: this.pointFill, - radius: this.pointRadius, + fill: this.themeConfig.quadrantPointFill, + radius: this.config.pointRadius, text: { text: point.text, - fill: this.pointTextFill, + fill: this.themeConfig.quadrantPointTextFill, x: xAxis(point.x), - y: yAxis(point.y) + this.pointTextPadding, + y: yAxis(point.y) + this.config.pointTextPadding, verticalPos: 'center', horizontalPos: 'top', - fontSize: this.pointLabelFontSize, + fontSize: this.config.pointLabelFontSize, rotation: 0, }, }; return props; }); - return { + const borderLines: QuadrantLineType[] = [ + // top border + { + strokeFill: this.themeConfig.quadrantExternalBorderStrokeFill, + strokeWidth: this.config.quadrantExternalBorderStrokeWidth, + x1: quadrantLeft - halfExternalBorderWidth, + y1: quadrantTop, + x2: quadrantLeft + quadrantWidth + halfExternalBorderWidth, + y2: quadrantTop, + }, + // right border + { + strokeFill: this.themeConfig.quadrantExternalBorderStrokeFill, + strokeWidth: this.config.quadrantExternalBorderStrokeWidth, + x1: quadrantLeft + quadrantWidth, + y1: quadrantTop + halfExternalBorderWidth, + x2: quadrantLeft + quadrantWidth, + y2: quadrantTop + quadrantHeight - halfExternalBorderWidth, + }, + // bottom border + { + strokeFill: this.themeConfig.quadrantExternalBorderStrokeFill, + strokeWidth: this.config.quadrantExternalBorderStrokeWidth, + x1: quadrantLeft - halfExternalBorderWidth, + y1: quadrantTop + quadrantHeight, + x2: quadrantLeft + quadrantWidth + halfExternalBorderWidth, + y2: quadrantTop + quadrantHeight, + }, + // left border + { + strokeFill: this.themeConfig.quadrantExternalBorderStrokeFill, + strokeWidth: this.config.quadrantExternalBorderStrokeWidth, + x1: quadrantLeft, + y1: quadrantTop + halfExternalBorderWidth, + x2: quadrantLeft, + y2: quadrantTop + quadrantHeight - halfExternalBorderWidth, + }, + // vertical inner border + { + strokeFill: this.themeConfig.quadrantInternalBorderStrokeFill, + strokeWidth: this.config.quadrantInternalBorderStrokeWidth, + x1: quadrantLeft + quadrantHalfWidth, + y1: quadrantTop + halfExternalBorderWidth, + x2: quadrantLeft + quadrantHalfWidth, + y2: quadrantTop + quadrantHeight - halfExternalBorderWidth, + }, + // horizontal inner border + { + strokeFill: this.themeConfig.quadrantInternalBorderStrokeFill, + strokeWidth: this.config.quadrantInternalBorderStrokeWidth, + x1: quadrantLeft + halfExternalBorderWidth, + y1: quadrantTop + quadrantHalfHeight, + x2: quadrantLeft + quadrantWidth - halfExternalBorderWidth, + y2: quadrantTop + quadrantHalfHeight, + }, + ]; + + const retVal: QuadrantBuildType = { points, quadrants, axisLabels, + borderLines, }; + + if (showTitle) { + retVal.title = { + text: this.config.titleText, + fill: this.themeConfig.quadrantTitleFill, + fontSize: this.config.titleFontSize, + horizontalPos: 'top', + verticalPos: 'center', + rotation: 0, + y: this.config.titlePadding, + x: this.config.chartWidth / 2, + }; + } + + return retVal; } } diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts index 630d6011c..53e3262fb 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts @@ -24,78 +24,73 @@ type LexTextObj = { text: string; type: 'text' | 'markdown' }; const quadrantBuilder = new QuadrantBuilder(); function setQuadrant1Text(textObj: LexTextObj) { - quadrantBuilder.quadrant1Text = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ quadrant1Text: textSanitizer(textObj.text) }); } function setQuadrant2Text(textObj: LexTextObj) { - quadrantBuilder.quadrant2Text = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ quadrant2Text: textSanitizer(textObj.text) }); } function setQuadrant3Text(textObj: LexTextObj) { - quadrantBuilder.quadrant3Text = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ quadrant3Text: textSanitizer(textObj.text) }); } function setQuadrant4Text(textObj: LexTextObj) { - quadrantBuilder.quadrant4Text = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ quadrant4Text: textSanitizer(textObj.text) }); } function setXAxisLeftText(textObj: LexTextObj) { - quadrantBuilder.xAxisLeftText = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ xAxisLeftText: textSanitizer(textObj.text) }); } function setXAxisRightText(textObj: LexTextObj) { - quadrantBuilder.xAxisRightText = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ xAxisRightText: textSanitizer(textObj.text) }); } function setYAxisTopText(textObj: LexTextObj) { - quadrantBuilder.yAxisTopText = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ yAxisTopText: textSanitizer(textObj.text) }); } function setYAxisBottomText(textObj: LexTextObj) { - quadrantBuilder.yAxisBottomText = textSanitizer(textObj.text); + quadrantBuilder.setConfig({ yAxisBottomText: textSanitizer(textObj.text) }); } -function addPoints(textObj: LexTextObj, x: number, y: number) { +function addPoint(textObj: LexTextObj, x: number, y: number) { quadrantBuilder.addPoints([{ x, y, text: textSanitizer(textObj.text) }]); } function setWidth(width: number) { - quadrantBuilder.totalWidth = width; + quadrantBuilder.setConfig({ chartWidth: width }); } function setHeight(height: number) { - quadrantBuilder.totalHeight = height; + quadrantBuilder.setConfig({ chartHeight: height }); } function getQuadrantData() { const config = configApi.getConfig(); const { themeVariables, quadrantChart: quadrantChartConfig } = config; - quadrantBuilder.quadrant1Fill = themeVariables.quadrant1Fill; - quadrantBuilder.quadrant2Fill = themeVariables.quadrant2Fill; - quadrantBuilder.quadrant3Fill = themeVariables.quadrant3Fill; - quadrantBuilder.quadrant4Fill = themeVariables.quadrant4Fill; - quadrantBuilder.quadrant1TextFill = themeVariables.quadrant1TextFill; - quadrantBuilder.quadrant2TextFill = themeVariables.quadrant2TextFill; - quadrantBuilder.quadrant3TextFill = themeVariables.quadrant3TextFill; - quadrantBuilder.quadrant4TextFill = themeVariables.quadrant4TextFill; - quadrantBuilder.pointFill = themeVariables.quadrantPointFill; - quadrantBuilder.pointTextFill = themeVariables.quadrantPointTextFill; - quadrantBuilder.xAxisTextFill = themeVariables.quadrantXAxisTextFill; - quadrantBuilder.yAxisTextFill = themeVariables.quadrantYAxisTextFill; if (quadrantChartConfig) { - quadrantBuilder.quadrantPadding = quadrantChartConfig.quadrantPadding; - quadrantBuilder.xAxisLabelPadding = quadrantChartConfig.xAxisLabelPadding; - quadrantBuilder.yAxisLabelPadding = quadrantChartConfig.yAxisLabelPadding; - quadrantBuilder.xAxisLabelFontSize = quadrantChartConfig.xAxisLabelFontSize; - quadrantBuilder.yAxisLabelFontSize = quadrantChartConfig.yAxisLabelFontSize; - quadrantBuilder.quadrantLabelFontSize = quadrantChartConfig.quadrantLabelFontSize; - quadrantBuilder.quadrantTextTopPadding = quadrantChartConfig.quadrantTextTopPadding; - quadrantBuilder.pointTextPadding = quadrantChartConfig.pointTextPadding; - quadrantBuilder.pointLabelFontSize = quadrantChartConfig.pointLabelFontSize; - quadrantBuilder.pointRadius = quadrantChartConfig.pointRadius; - quadrantBuilder.xAxisPosition = quadrantChartConfig.xAxisPosition; - quadrantBuilder.yAxisPosition = quadrantChartConfig.yAxisPosition; + quadrantBuilder.setConfig(quadrantChartConfig); } + quadrantBuilder.setThemeConfig({ + quadrant1Fill: themeVariables.quadrant1Fill, + quadrant2Fill: themeVariables.quadrant2Fill, + quadrant3Fill: themeVariables.quadrant3Fill, + quadrant4Fill: themeVariables.quadrant4Fill, + quadrant1TextFill: themeVariables.quadrant1TextFill, + quadrant2TextFill: themeVariables.quadrant2TextFill, + quadrant3TextFill: themeVariables.quadrant3TextFill, + quadrant4TextFill: themeVariables.quadrant4TextFill, + quadrantPointFill: themeVariables.quadrantPointFill, + quadrantPointTextFill: themeVariables.quadrantPointTextFill, + quadrantXAxisTextFill: themeVariables.quadrantXAxisTextFill, + quadrantYAxisTextFill: themeVariables.quadrantYAxisTextFill, + quadrantExternalBorderStrokeFill: themeVariables.quadrantExternalBorderStrokeFill, + quadrantInternalBorderStrokeFill: themeVariables.quadrantInternalBorderStrokeFill, + quadrantTitleFill: themeVariables.quadrantTitleFill, + }); + quadrantBuilder.setConfig({ titleText: getDiagramTitle() }); return quadrantBuilder.build(); } @@ -120,7 +115,7 @@ export default { setXAxisRightText, setYAxisTopText, setYAxisBottomText, - addPoints, + addPoint, getQuadrantData, parseDirective, clear, diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts index 72639ba2e..b01df3022 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts @@ -7,6 +7,7 @@ import { configureSvgSize } from '../../setupGraphViewbox.js'; import { Diagram } from '../../Diagram.js'; import { QuadrantBuildType, + QuadrantLineType, QuadrantPointType, QuadrantQuadrantsType, QuadrantTextType, @@ -29,7 +30,7 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram const conf = configApi.getConfig(); - log.debug('Rendering info diagram\n' + txt); + log.debug('Rendering quadrant chart\n' + txt); const securityLevel = conf.securityLevel; // Handle root and Document for when rendering in sandbox mode @@ -62,8 +63,37 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram const quadrantData: QuadrantBuildType = diagObj.db.getQuadrantData(); const quadrantsGroup = group.append('g').attr('class', 'quadrants'); + const borderGroup = group.append('g').attr('class', 'border'); const dataPointGroup = group.append('g').attr('class', 'data-points'); const labelGroup = group.append('g').attr('class', 'labels'); + const titleGroup = group.append('g').attr('class', 'title'); + + if (quadrantData.title) { + titleGroup + .append('text') + .attr('x', 0) + .attr('y', 0) + .attr('fill', quadrantData.title.fill) + .attr('font-size', quadrantData.title.fontSize) + .attr('dominant-baseline', getDominantBaseLine(quadrantData.title.horizontalPos)) + .attr('text-anchor', getTextAnchor(quadrantData.title.verticalPos)) + .attr('transform', getTransformation(quadrantData.title)) + .text(quadrantData.title.text); + } + + if (quadrantData.borderLines) { + borderGroup + .selectAll('line') + .data(quadrantData.borderLines) + .enter() + .append('line') + .attr('x1', (data: QuadrantLineType) => data.x1) + .attr('y1', (data: QuadrantLineType) => data.y1) + .attr('x2', (data: QuadrantLineType) => data.x2) + .attr('y2', (data: QuadrantLineType) => data.y2) + .style('stroke', (data: QuadrantLineType) => data.strokeFill) + .style('stroke-width', (data: QuadrantLineType) => data.strokeWidth); + } const quadrants = quadrantsGroup .selectAll('g.quadrant') diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index 53c5f73b6..98c740d51 100644 --- a/packages/mermaid/src/themes/theme-base.js +++ b/packages/mermaid/src/themes/theme-base.js @@ -239,6 +239,11 @@ class Theme { this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + this.quadrantInternalBorderStrokeFill = + this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; + this.quadrantExternalBorderStrokeFill = + this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; diff --git a/packages/mermaid/src/themes/theme-dark.js b/packages/mermaid/src/themes/theme-dark.js index d1f5dbde6..b05308d2e 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -245,6 +245,11 @@ class Theme { this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + this.quadrantInternalBorderStrokeFill = + this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; + this.quadrantExternalBorderStrokeFill = + this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* class */ this.classText = this.primaryTextColor; diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index f73a05c4d..326338aee 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -266,6 +266,11 @@ class Theme { this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + this.quadrantInternalBorderStrokeFill = + this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; + this.quadrantExternalBorderStrokeFill = + this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; diff --git a/packages/mermaid/src/themes/theme-forest.js b/packages/mermaid/src/themes/theme-forest.js index f3e41adec..9dda643ff 100644 --- a/packages/mermaid/src/themes/theme-forest.js +++ b/packages/mermaid/src/themes/theme-forest.js @@ -234,6 +234,11 @@ class Theme { this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + this.quadrantInternalBorderStrokeFill = + this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; + this.quadrantExternalBorderStrokeFill = + this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index f240404a7..43d0b61b5 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -265,6 +265,11 @@ class Theme { this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; + this.quadrantInternalBorderStrokeFill = + this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; + this.quadrantExternalBorderStrokeFill = + this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ this.requirementBackground = this.requirementBackground || this.primaryColor; From fe9c109837007a609aef4ed7d03f5c4bc40d09b3 Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Fri, 12 May 2023 17:09:14 +0530 Subject: [PATCH 5/9] Fixed some parser issue and added test cases for the parser --- .../quadrant-chart/parser/quadrant.jison | 30 +- .../parser/quadrant.jison.spec.ts | 298 ++++++++++++++++++ 2 files changed, 314 insertions(+), 14 deletions(-) create mode 100644 packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison index 67c47cb13..6967deb26 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison @@ -42,13 +42,13 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} <acc_descr_multiline>[\}] { this.popState(); } <acc_descr_multiline>[^\}]* return "acc_descr_multiline_value"; -\s*"x-axis"\s* return 'X-AXIS'; -\s*"y-axis"\s* return 'Y-AXIS'; -\s*\-\-+\>[^(\r?\n)\s]* return 'AXIS-TEXT-DELIMITER' -\s*"quadrant-1"\s* return 'QUADRANT_1'; -\s*"quadrant-2"\s* return 'QUADRANT_2'; -\s*"quadrant-3"\s* return 'QUADRANT_3'; -\s*"quadrant-4"\s* return 'QUADRANT_4'; +" "*"x-axis"" "* return 'X-AXIS'; +" "*"y-axis"" "* return 'Y-AXIS'; +" "*\-\-+\>" "* return 'AXIS-TEXT-DELIMITER' +" "*"quadrant-1"" "* return 'QUADRANT_1'; +" "*"quadrant-2"" "* return 'QUADRANT_2'; +" "*"quadrant-3"" "* return 'QUADRANT_3'; +" "*"quadrant-4"" "* return 'QUADRANT_4'; ["][`] { this.begin("md_string");} <md_string>[^`"]+ { return "MD_STR";} @@ -59,13 +59,12 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} \s*\:\s*\[\s* {this.begin("point_start"); return 'point_start';} <point_start>(1)|(0(.\d+)?) {this.begin('point_x'); return 'point_x';} -<point_start>\s*\] {this.popState();} +<point_start>\s*\]" "* {this.popState();} <point_x>\s*\,\s* {this.popState(); this.begin('point_y');} <point_y>(1)|(0(.\d+)?) {this.popState(); return 'point_y';} -"quadrantChart"\s* return 'QUADRANT'; +" "*"quadrantChart"" "* return 'QUADRANT'; -\[[0-1].?[0-9]{5}\] return 'POINT_VALUE' [A-Za-z]+ return 'ALPHA'; ":" return 'COLON'; \+ return 'PLUS'; @@ -92,6 +91,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} start : eol start + | SPACE start | directive start | QUADRANT document ; @@ -102,11 +102,12 @@ document ; line - : statement eol { $$ = $1 } + : statement eol ; statement : + | SPACE statement | axisDetails | quadrantDetails | points @@ -118,7 +119,8 @@ statement ; points - : text point_start point_x point_y {yy.addPoint($1, $3, $4);}; + : text point_start point_x point_y {yy.addPoint($1, $3, $4);} + ; axisDetails : X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);} @@ -160,10 +162,10 @@ argDirective ; closeDirective - : close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); } + : close_directive { yy.parseDirective('}%%', 'close_directive', 'quadrantChart'); } ; -text: textNoTagsToken +text: alphaNumToken { $$={text:$1, type: 'text'};} | text textNoTagsToken { $$={text:$1.text+''+$2, type: $1.type};} diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts new file mode 100644 index 000000000..e6068d71e --- /dev/null +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts @@ -0,0 +1,298 @@ +// @ts-ignore +import { parser } from './quadrant.jison'; +import { Mock, vi } from 'vitest'; + +const parserFnConstructor = (str: string) => { + return () => { + parser.parse(str); + }; +}; + +const mockDB: Record<string, Mock<any, any>> = { + setQuadrant1Text: vi.fn(), + setQuadrant2Text: vi.fn(), + setQuadrant3Text: vi.fn(), + setQuadrant4Text: vi.fn(), + setXAxisLeftText: vi.fn(), + setXAxisRightText: vi.fn(), + setYAxisTopText: vi.fn(), + setYAxisBottomText: vi.fn(), + setDiagramTitle: vi.fn(), + parseDirective: vi.fn(), + addPoint: vi.fn(), +}; + +function clearMocks() { + for (const key in mockDB) { + mockDB[key].mockRestore(); + } +} + +describe('Testing quadrantChart jison file', () => { + beforeEach(() => { + parser.yy = mockDB; + clearMocks(); + }); + + it('should throw error if quadrantChart text is not there', () => { + const str = 'quadrant-1 do'; + expect(parserFnConstructor(str)).toThrow(); + }); + + it('should not throw error if only quadrantChart is there', () => { + const str = 'quadrantChart'; + expect(parserFnConstructor(str)).not.toThrow(); + }); + + it('should be able to parse directive', () => { + let str = + '%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% \n quadrantChart'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']); + expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']); + expect(mockDB.parseDirective.mock.calls[2]).toEqual([ + '{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }', + 'arg_directive', + ]); + expect(mockDB.parseDirective.mock.calls[3]).toEqual([ + '}%%', + 'close_directive', + 'quadrantChart', + ]); + }); + + it('should be able to parse xAxis text', () => { + let str = 'quadrantChart\nx-axis urgent --> not urgent'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'urgent', type: 'text' }); + expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'not urgent', type: 'text' }); + + clearMocks(); + str = 'quadrantChart\n x-AxIs Urgent --> Not Urgent \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'Urgent', type: 'text' }); + expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'Not Urgent ', type: 'text' }); + + clearMocks(); + str = + 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> "Not Urgent (* +=[❤"\n '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' }); + expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ + text: 'Not Urgent (* +=[❤', + type: 'text', + }); + + clearMocks(); + str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' }); + expect(mockDB.setXAxisRightText).not.toHaveBeenCalled(); + + clearMocks(); + str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ + text: 'Urgent(* +=[❤ --> ', + type: 'text', + }); + expect(mockDB.setXAxisRightText).not.toHaveBeenCalled(); + }); + + it('should be able to parse yAxis text', () => { + let str = 'quadrantChart\ny-axis urgent --> not urgent'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'urgent', type: 'text' }); + expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'not urgent', type: 'text' }); + + clearMocks(); + str = 'quadrantChart\n y-AxIs Urgent --> Not Urgent \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'Urgent', type: 'text' }); + expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'Not Urgent ', type: 'text' }); + + clearMocks(); + str = + 'quadrantChart\n Y-AxIs "Urgent(* +=[❤" --> "Not Urgent (* +=[❤"\n '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' }); + expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ + text: 'Not Urgent (* +=[❤', + type: 'text', + }); + + clearMocks(); + str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' }); + expect(mockDB.setYAxisTopText).not.toHaveBeenCalled(); + + clearMocks(); + str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤" --> '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ + text: 'Urgent(* +=[❤ --> ', + type: 'text', + }); + expect(mockDB.setYAxisTopText).not.toHaveBeenCalled(); + }); + + it('should be able to parse quadrant1 text', () => { + let str = 'quadrantChart\nquadrant-1 Plan'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Plan', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-1 Plan '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Plan ', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-1 "Plan(* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Plan(* +=[❤', type: 'text' }); + }); + + it('should be able to parse quadrant2 text', () => { + let str = 'quadrantChart\nquadrant-2 do'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'do', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-2 Do '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Do ', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-2 "Do(* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Do(* +=[❤', type: 'text' }); + }); + + it('should be able to parse quadrant3 text', () => { + let str = 'quadrantChart\nquadrant-3 deligate'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'deligate', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-3 Deligate '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Deligate ', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-3 "Deligate(* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Deligate(* +=[❤', type: 'text' }); + }); + + it('should be able to parse quadrant4 text', () => { + let str = 'quadrantChart\nquadrant-4 delete'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'delete', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-4 Delete '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Delete ', type: 'text' }); + + clearMocks(); + str = 'QuadRantChart \n QuaDrant-4 "Delete(* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Delete(* +=[❤', type: 'text' }); + }); + + it('should be able to parse title', () => { + let str = 'quadrantChart\ntitle this is title'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('this is title'); + + clearMocks(); + str = 'QuadRantChart \n TiTle this Is title '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('this Is title'); + + clearMocks(); + str = 'QuadRantChart \n title "this is title (* +=[❤"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('"this is title (* +=[❤"'); + }); + + it('should be able to parse points', () => { + let str = 'quadrantChart\npoint1: [0.1, 0.4]'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'point1', type: 'text' }, '0.1', '0.4'); + + clearMocks(); + str = 'QuadRantChart \n Point1 : [0.1, 0.4] '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'Point1', type: 'text' }, '0.1', '0.4'); + + clearMocks(); + str = 'QuadRantChart \n "Point1 : (* +=[❤": [1, 0] '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.addPoint).toHaveBeenCalledWith( + { text: 'Point1 : (* +=[❤', type: 'text' }, + '1', + '0' + ); + + clearMocks(); + str = 'QuadRantChart \n Point1 : [1.2, 0.4] '; + expect(parserFnConstructor(str)).toThrow(); + }); + + it('should be able to parse the whole chart', () => { + let str = `%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% + quadrantChart + title Analytics and Business Intelligence Platforms + x-axis "Completeness of Vision ❤" --> "x-axis-2" + y-axis Ability to Execute --> "y-axis-2" + quadrant-1 Leaders + quadrant-2 Challengers + quadrant-3 Niche + quadrant-4 Visionaries + Microsoft: [0.75, 0.75] + Salesforce: [0.55, 0.60] + IBM: [0.51, 0.40] + Incorta: [0.20, 0.30]`; + + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']); + expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']); + expect(mockDB.parseDirective.mock.calls[2]).toEqual([ + '{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }', + 'arg_directive', + ]); + expect(mockDB.parseDirective.mock.calls[3]).toEqual([ + '}%%', + 'close_directive', + 'quadrantChart', + ]); + expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ + text: 'Completeness of Vision ❤', + type: 'text', + }); + expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'x-axis-2', type: 'text' }); + expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'y-axis-2', type: 'text' }); + expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ + text: 'Ability to Execute', + type: 'text', + }); + expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Leaders', type: 'text' }); + expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Challengers', type: 'text' }); + expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Niche', type: 'text' }); + expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Visionaries', type: 'text' }); + expect(mockDB.addPoint).toHaveBeenCalledWith( + { text: 'Microsoft', type: 'text' }, + '0.75', + '0.75' + ); + expect(mockDB.addPoint).toHaveBeenCalledWith( + { text: 'Salesforce', type: 'text' }, + '0.55', + '0.60' + ); + expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'IBM', type: 'text' }, '0.51', '0.40'); + expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'Incorta', type: 'text' }, '0.20', '0.30'); + }); +}); From 816800adcd1513f4a60b631850724ef35cd1f89c Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Fri, 12 May 2023 19:32:39 +0530 Subject: [PATCH 6/9] Restructured the build function and addressed more review comment --- .../quadrant-chart/parser/quadrant.jison | 5 - .../parser/quadrant.jison.spec.ts | 6 +- .../quadrant-chart/quadrantBuilder.ts | 297 ++++++++++++------ .../src/diagrams/quadrant-chart/quadrantDb.ts | 18 +- .../quadrant-chart/quadrantRenderer.ts | 5 +- packages/mermaid/src/themes/theme-base.js | 14 +- packages/mermaid/src/themes/theme-dark.js | 14 +- packages/mermaid/src/themes/theme-default.js | 14 +- packages/mermaid/src/themes/theme-forest.js | 14 +- packages/mermaid/src/themes/theme-neutral.js | 14 +- 10 files changed, 252 insertions(+), 149 deletions(-) diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison index 6967deb26..555eea152 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison @@ -1,8 +1,3 @@ -/** mermaid - * https://knsv.github.io/mermaid - * (c) 2015 Knut Sveidqvist - * MIT license. - */ %lex %options case-insensitive diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts index e6068d71e..f91858c72 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts @@ -1,4 +1,4 @@ -// @ts-ignore +// @ts-ignore: TODO Fix ts errors import { parser } from './quadrant.jison'; import { Mock, vi } from 'vitest'; @@ -45,7 +45,7 @@ describe('Testing quadrantChart jison file', () => { }); it('should be able to parse directive', () => { - let str = + const str = '%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% \n quadrantChart'; expect(parserFnConstructor(str)).not.toThrow(); expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']); @@ -242,7 +242,7 @@ describe('Testing quadrantChart jison file', () => { }); it('should be able to parse the whole chart', () => { - let str = `%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% + const str = `%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% quadrantChart title Analytics and Business Intelligence Platforms x-axis "Completeness of Vision ❤" --> "x-axis-2" diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts index 231af941b..95b8c6679 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts @@ -2,35 +2,45 @@ import { scaleLinear } from 'd3'; import { log } from '../../logger.js'; import { QuadrantChartConfig } from '../../config.type.js'; +import defaultConfig from '../../defaultConfig.js'; +import { getThemeVariables } from '../../themes/theme-default.js'; -export interface QuadrantPointInputType { - x: number; - y: number; - text: string; -} +const defaultThemeVariables = getThemeVariables(); export type TextVerticalPos = 'left' | 'center' | 'right'; export type TextHorizontalPos = 'top' | 'middle' | 'bottom'; -export interface QuadrantTextType { - text: string; - fill: string; +export interface Point { x: number; y: number; +} + +export interface QuadrantPointInputType extends Point { + text: string; +} + +export interface QuadrantTextType extends Point { + text: string; + fill: string; verticalPos: TextVerticalPos; horizontalPos: TextHorizontalPos; fontSize: number; rotation: number; } -export interface QuadrantPointType { - x: number; - y: number; +export interface QuadrantPointType extends Point { fill: string; radius: number; text: QuadrantTextType; } +export interface QuadrantQuadrantsType extends Point { + text: QuadrantTextType; + width: number; + height: number; + fill: string; +} + export interface QuadrantLineType { strokeWidth: number; strokeFill: string; @@ -40,15 +50,6 @@ export interface QuadrantLineType { y2: number; } -export interface QuadrantQuadrantsType { - text: QuadrantTextType; - x: number; - y: number; - width: number; - height: number; - fill: string; -} - export interface QuadrantBuildType { points: QuadrantPointType[]; quadrants: QuadrantQuadrantsType[]; @@ -57,7 +58,7 @@ export interface QuadrantBuildType { borderLines?: QuadrantLineType[]; } -export interface QuadrantBuilderConfig extends QuadrantChartConfig { +export interface quadrantBuilderData { titleText: string; quadrant1Text: string; quadrant2Text: string; @@ -68,6 +69,9 @@ export interface QuadrantBuilderConfig extends QuadrantChartConfig { yAxisBottomText: string; yAxisTopText: string; points: QuadrantPointInputType[]; +} + +export interface QuadrantBuilderConfig extends QuadrantChartConfig { showXAxis: boolean; showYAxis: boolean; showTitle: boolean; @@ -91,16 +95,40 @@ export interface QuadrantBuilderThemeConfig { quadrantExternalBorderStrokeFill: string; } +interface CalculateSpaceData { + xAxisSpace: { + top: number; + bottom: number; + }; + yAxisSpace: { + left: number; + right: number; + }; + titleSpace: { + top: number; + }; + quadrantSpace: { + quadrantLeft: number; + quadrantTop: number; + quadrantWidth: number; + quadrantHalfWidth: number; + quadrantHeight: number; + quadrantHalfHeight: number; + }; +} + export class QuadrantBuilder { private config: QuadrantBuilderConfig; private themeConfig: QuadrantBuilderThemeConfig; + private data: quadrantBuilderData; constructor() { this.config = this.getDefaultConfig(); this.themeConfig = this.getDefaultThemeConfig(); + this.data = this.getDefaultData(); } - getDefaultConfig(): QuadrantBuilderConfig { + getDefaultData(): quadrantBuilderData { return { titleText: '', quadrant1Text: '', @@ -112,58 +140,70 @@ export class QuadrantBuilder { yAxisBottomText: '', yAxisTopText: '', points: [], + }; + } + + getDefaultConfig(): QuadrantBuilderConfig { + return { showXAxis: true, showYAxis: true, showTitle: true, - chartHeight: 500, - chartWidth: 500, - titlePadding: 5, - titleFontSize: 20, - quadrantPadding: 5, - xAxisLabelPadding: 5, - yAxisLabelPadding: 5, - xAxisLabelFontSize: 16, - yAxisLabelFontSize: 16, - quadrantLabelFontSize: 16, - quadrantTextTopPadding: 5, - pointTextPadding: 5, - pointLabelFontSize: 12, - pointRadius: 5, - xAxisPosition: 'top', - yAxisPosition: 'left', - quadrantInternalBorderStrokeWidth: 2, - quadrantExternalBorderStrokeWidth: 3, + chartHeight: defaultConfig.quadrantChart?.chartWidth || 500, + chartWidth: defaultConfig.quadrantChart?.chartHeight || 500, + titlePadding: defaultConfig.quadrantChart?.titlePadding || 5, + titleFontSize: defaultConfig.quadrantChart?.titleFontSize || 20, + quadrantPadding: defaultConfig.quadrantChart?.quadrantPadding || 5, + xAxisLabelPadding: defaultConfig.quadrantChart?.xAxisLabelPadding || 5, + yAxisLabelPadding: defaultConfig.quadrantChart?.yAxisLabelPadding || 5, + xAxisLabelFontSize: defaultConfig.quadrantChart?.xAxisLabelFontSize || 16, + yAxisLabelFontSize: defaultConfig.quadrantChart?.yAxisLabelFontSize || 16, + quadrantLabelFontSize: defaultConfig.quadrantChart?.quadrantLabelFontSize || 16, + quadrantTextTopPadding: defaultConfig.quadrantChart?.quadrantTextTopPadding || 5, + pointTextPadding: defaultConfig.quadrantChart?.pointTextPadding || 5, + pointLabelFontSize: defaultConfig.quadrantChart?.pointLabelFontSize || 12, + pointRadius: defaultConfig.quadrantChart?.pointRadius || 5, + xAxisPosition: defaultConfig.quadrantChart?.xAxisPosition || 'top', + yAxisPosition: defaultConfig.quadrantChart?.yAxisPosition || 'left', + quadrantInternalBorderStrokeWidth: + defaultConfig.quadrantChart?.quadrantInternalBorderStrokeWidth || 1, + quadrantExternalBorderStrokeWidth: + defaultConfig.quadrantChart?.quadrantExternalBorderStrokeWidth || 2, }; } getDefaultThemeConfig(): QuadrantBuilderThemeConfig { return { - quadrant1Fill: '#8bc2f3', - quadrant2Fill: '#faebd7', - quadrant3Fill: '#00ffff', - quadrant4Fill: '#f0ffff', - quadrant1TextFill: '#93690e', - quadrant2TextFill: '#8644ff', - quadrant3TextFill: '#e3004d', - quadrant4TextFill: '#000000', - quadrantPointFill: '#60B19C', - quadrantPointTextFill: '#0000ff', - quadrantXAxisTextFill: '#000000', - quadrantYAxisTextFill: '#000000', - quadrantTitleFill: '#000000', - quadrantInternalBorderStrokeFill: '#000000', - quadrantExternalBorderStrokeFill: '#000000', + quadrant1Fill: defaultThemeVariables.quadrant1Fill, + quadrant2Fill: defaultThemeVariables.quadrant2Fill, + quadrant3Fill: defaultThemeVariables.quadrant3Fill, + quadrant4Fill: defaultThemeVariables.quadrant4Fill, + quadrant1TextFill: defaultThemeVariables.quadrant1TextFill, + quadrant2TextFill: defaultThemeVariables.quadrant2TextFill, + quadrant3TextFill: defaultThemeVariables.quadrant3TextFill, + quadrant4TextFill: defaultThemeVariables.quadrant4TextFill, + quadrantPointFill: defaultThemeVariables.quadrantPointFill, + quadrantPointTextFill: defaultThemeVariables.quadrantPointTextFill, + quadrantXAxisTextFill: defaultThemeVariables.quadrantXAxisTextFill, + quadrantYAxisTextFill: defaultThemeVariables.quadrantYAxisTextFill, + quadrantTitleFill: defaultThemeVariables.quadrantTitleFill, + quadrantInternalBorderStrokeFill: defaultThemeVariables.quadrantInternalBorderStrokeFill, + quadrantExternalBorderStrokeFill: defaultThemeVariables.quadrantExternalBorderStrokeFill, }; } clear() { this.config = this.getDefaultConfig(); this.themeConfig = this.getDefaultThemeConfig(); + this.data = this.getDefaultData(); log.info('clear called'); } + setData(data: Partial<quadrantBuilderData>) { + this.data = { ...this.data, ...data }; + } + addPoints(points: QuadrantPointInputType[]) { - this.config.points = [...points, ...this.config.points]; + this.data.points = [...points, ...this.data.points]; } setConfig(config: Partial<QuadrantBuilderConfig>) { @@ -176,20 +216,12 @@ export class QuadrantBuilder { this.themeConfig = { ...this.themeConfig, ...themeConfig }; } - build(): QuadrantBuildType { - const showXAxis = - this.config.showXAxis && (this.config.xAxisLeftText || this.config.xAxisRightText); - const showYAxis = - this.config.showYAxis && (this.config.yAxisTopText || this.config.yAxisBottomText); - const showTitle = this.config.showTitle && this.config.titleText; - - const halfExternalBorderWidth = this.config.quadrantExternalBorderStrokeWidth / 2; - const halfInternalBorderWidth = this.config.quadrantInternalBorderStrokeWidth / 2; - - const xAxisPosition = this.config.points.length > 0 ? 'bottom' : this.config.xAxisPosition; - - const drawAxisLabelInMiddle = this.config.points.length === 0; - + calculateSpace( + xAxisPosition: typeof this.config.xAxisPosition, + showXAxis: boolean, + showYAxis: boolean, + showTitle: boolean + ): CalculateSpaceData { const xAxisSpaceCalculation = this.config.xAxisLabelPadding * 2 + this.config.xAxisLabelFontSize; const xAxisSpace = { @@ -222,12 +254,44 @@ export class QuadrantBuilder { const quadrantHalfWidth = quadrantWidth / 2; const quadrantHalfHeight = quadrantHeight / 2; + const quadrantSpace = { + quadrantLeft, + quadrantTop, + quadrantWidth, + quadrantHalfWidth, + quadrantHeight, + quadrantHalfHeight, + }; + return { + xAxisSpace, + yAxisSpace, + titleSpace, + quadrantSpace, + }; + } + + getAxisLabels( + xAxisPosition: typeof this.config.xAxisPosition, + showXAxis: boolean, + showYAxis: boolean, + spaceData: CalculateSpaceData + ): QuadrantTextType[] { + const { quadrantSpace, titleSpace } = spaceData; + const { + quadrantHalfHeight, + quadrantHeight, + quadrantLeft, + quadrantHalfWidth, + quadrantTop, + quadrantWidth, + } = quadrantSpace; + const drawAxisLabelInMiddle = this.data.points.length === 0; const axisLabels: QuadrantTextType[] = []; - if (this.config.xAxisLeftText && showXAxis) { + if (this.data.xAxisLeftText && showXAxis) { axisLabels.push({ - text: this.config.xAxisLeftText, + text: this.data.xAxisLeftText, fill: this.themeConfig.quadrantXAxisTextFill, x: quadrantLeft + (drawAxisLabelInMiddle ? quadrantHalfWidth / 2 : 0), y: @@ -240,9 +304,9 @@ export class QuadrantBuilder { rotation: 0, }); } - if (this.config.xAxisRightText && showXAxis) { + if (this.data.xAxisRightText && showXAxis) { axisLabels.push({ - text: this.config.xAxisRightText, + text: this.data.xAxisRightText, fill: this.themeConfig.quadrantXAxisTextFill, x: quadrantLeft + quadrantHalfWidth + (drawAxisLabelInMiddle ? quadrantHalfWidth / 2 : 0), y: @@ -256,9 +320,9 @@ export class QuadrantBuilder { }); } - if (this.config.yAxisBottomText && showYAxis) { + if (this.data.yAxisBottomText && showYAxis) { axisLabels.push({ - text: this.config.yAxisBottomText, + text: this.data.yAxisBottomText, fill: this.themeConfig.quadrantYAxisTextFill, x: this.config.yAxisPosition === 'left' @@ -271,9 +335,9 @@ export class QuadrantBuilder { rotation: -90, }); } - if (this.config.yAxisTopText && showYAxis) { + if (this.data.yAxisTopText && showYAxis) { axisLabels.push({ - text: this.config.yAxisTopText, + text: this.data.yAxisTopText, fill: this.themeConfig.quadrantYAxisTextFill, x: this.config.yAxisPosition === 'left' @@ -286,11 +350,18 @@ export class QuadrantBuilder { rotation: -90, }); } + return axisLabels; + } + + getQuadrants(spaceData: CalculateSpaceData): QuadrantQuadrantsType[] { + const { quadrantSpace } = spaceData; + + const { quadrantHalfHeight, quadrantLeft, quadrantHalfWidth, quadrantTop } = quadrantSpace; const quadrants: QuadrantQuadrantsType[] = [ { text: { - text: this.config.quadrant1Text, + text: this.data.quadrant1Text, fill: this.themeConfig.quadrant1TextFill, x: 0, y: 0, @@ -307,7 +378,7 @@ export class QuadrantBuilder { }, { text: { - text: this.config.quadrant2Text, + text: this.data.quadrant2Text, fill: this.themeConfig.quadrant2TextFill, x: 0, y: 0, @@ -324,7 +395,7 @@ export class QuadrantBuilder { }, { text: { - text: this.config.quadrant3Text, + text: this.data.quadrant3Text, fill: this.themeConfig.quadrant3TextFill, x: 0, y: 0, @@ -341,7 +412,7 @@ export class QuadrantBuilder { }, { text: { - text: this.config.quadrant4Text, + text: this.data.quadrant4Text, fill: this.themeConfig.quadrant4TextFill, x: 0, y: 0, @@ -360,7 +431,7 @@ export class QuadrantBuilder { for (const quadrant of quadrants) { quadrant.text.x = quadrant.x + quadrant.width / 2; // place the text in the center of the box - if (this.config.points.length === 0) { + if (this.data.points.length === 0) { quadrant.text.y = quadrant.y + quadrant.height / 2; quadrant.text.horizontalPos = 'middle'; // place the text top of the quadrant square @@ -370,6 +441,14 @@ export class QuadrantBuilder { } } + return quadrants; + } + + getQuadrantPoints(spaceData: CalculateSpaceData): QuadrantPointType[] { + const { quadrantSpace } = spaceData; + + const { quadrantHeight, quadrantLeft, quadrantTop, quadrantWidth } = quadrantSpace; + const xAxis = scaleLinear() .domain([0, 1]) .range([quadrantLeft, quadrantWidth + quadrantLeft]); @@ -378,7 +457,7 @@ export class QuadrantBuilder { .domain([0, 1]) .range([quadrantHeight + quadrantTop, quadrantTop]); - const points: QuadrantPointType[] = this.config.points.map((point) => { + const points: QuadrantPointType[] = this.data.points.map((point) => { const props: QuadrantPointType = { x: xAxis(point.x), y: yAxis(point.y), @@ -397,6 +476,21 @@ export class QuadrantBuilder { }; return props; }); + return points; + } + + getBorders(spaceData: CalculateSpaceData): QuadrantLineType[] { + const halfExternalBorderWidth = this.config.quadrantExternalBorderStrokeWidth / 2; + const { quadrantSpace } = spaceData; + + const { + quadrantHalfHeight, + quadrantHeight, + quadrantLeft, + quadrantHalfWidth, + quadrantTop, + quadrantWidth, + } = quadrantSpace; const borderLines: QuadrantLineType[] = [ // top border @@ -454,17 +548,13 @@ export class QuadrantBuilder { y2: quadrantTop + quadrantHalfHeight, }, ]; + return borderLines; + } - const retVal: QuadrantBuildType = { - points, - quadrants, - axisLabels, - borderLines, - }; - + getTitle(showTitle: boolean): QuadrantTextType | undefined { if (showTitle) { - retVal.title = { - text: this.config.titleText, + return { + text: this.data.titleText, fill: this.themeConfig.quadrantTitleFill, fontSize: this.config.titleFontSize, horizontalPos: 'top', @@ -474,7 +564,26 @@ export class QuadrantBuilder { x: this.config.chartWidth / 2, }; } + return; + } - return retVal; + build(): QuadrantBuildType { + const showXAxis = + this.config.showXAxis && !!(this.data.xAxisLeftText || this.data.xAxisRightText); + const showYAxis = + this.config.showYAxis && !!(this.data.yAxisTopText || this.data.yAxisBottomText); + const showTitle = this.config.showTitle && !!this.data.titleText; + + const xAxisPosition = this.data.points.length > 0 ? 'bottom' : this.config.xAxisPosition; + + const calculatedSpace = this.calculateSpace(xAxisPosition, showXAxis, showYAxis, showTitle); + + return { + points: this.getQuadrantPoints(calculatedSpace), + quadrants: this.getQuadrants(calculatedSpace), + axisLabels: this.getAxisLabels(xAxisPosition, showXAxis, showYAxis, calculatedSpace), + borderLines: this.getBorders(calculatedSpace), + title: this.getTitle(showTitle), + }; } } diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts index 53e3262fb..c0c0f4c8a 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts @@ -24,35 +24,35 @@ type LexTextObj = { text: string; type: 'text' | 'markdown' }; const quadrantBuilder = new QuadrantBuilder(); function setQuadrant1Text(textObj: LexTextObj) { - quadrantBuilder.setConfig({ quadrant1Text: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ quadrant1Text: textSanitizer(textObj.text) }); } function setQuadrant2Text(textObj: LexTextObj) { - quadrantBuilder.setConfig({ quadrant2Text: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ quadrant2Text: textSanitizer(textObj.text) }); } function setQuadrant3Text(textObj: LexTextObj) { - quadrantBuilder.setConfig({ quadrant3Text: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ quadrant3Text: textSanitizer(textObj.text) }); } function setQuadrant4Text(textObj: LexTextObj) { - quadrantBuilder.setConfig({ quadrant4Text: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ quadrant4Text: textSanitizer(textObj.text) }); } function setXAxisLeftText(textObj: LexTextObj) { - quadrantBuilder.setConfig({ xAxisLeftText: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ xAxisLeftText: textSanitizer(textObj.text) }); } function setXAxisRightText(textObj: LexTextObj) { - quadrantBuilder.setConfig({ xAxisRightText: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ xAxisRightText: textSanitizer(textObj.text) }); } function setYAxisTopText(textObj: LexTextObj) { - quadrantBuilder.setConfig({ yAxisTopText: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ yAxisTopText: textSanitizer(textObj.text) }); } function setYAxisBottomText(textObj: LexTextObj) { - quadrantBuilder.setConfig({ yAxisBottomText: textSanitizer(textObj.text) }); + quadrantBuilder.setData({ yAxisBottomText: textSanitizer(textObj.text) }); } function addPoint(textObj: LexTextObj, x: number, y: number) { @@ -90,7 +90,7 @@ function getQuadrantData() { quadrantInternalBorderStrokeFill: themeVariables.quadrantInternalBorderStrokeFill, quadrantTitleFill: themeVariables.quadrantTitleFill, }); - quadrantBuilder.setConfig({ titleText: getDiagramTitle() }); + quadrantBuilder.setData({ titleText: getDiagramTitle() }); return quadrantBuilder.build(); } diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts index b01df3022..955f77f54 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts @@ -2,7 +2,6 @@ import { select } from 'd3'; import * as configApi from '../../config.js'; import { log } from '../../logger.js'; - import { configureSvgSize } from '../../setupGraphViewbox.js'; import { Diagram } from '../../Diagram.js'; import { @@ -16,8 +15,8 @@ import { } from './quadrantBuilder.js'; export const draw = (txt: string, id: string, _version: string, diagObj: Diagram) => { - function getDominantBaseLine(horizintalPos: TextHorizontalPos) { - return horizintalPos === 'top' ? 'text-before-edge' : 'middle'; + function getDominantBaseLine(horizontalPos: TextHorizontalPos) { + return horizontalPos === 'top' ? 'text-before-edge' : 'middle'; } function getTextAnchor(verticalPos: TextVerticalPos) { diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index 98c740d51..495df2e41 100644 --- a/packages/mermaid/src/themes/theme-base.js +++ b/packages/mermaid/src/themes/theme-base.js @@ -222,16 +222,16 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.primaryColor, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.primaryColor, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; this.quadrant2TextFill = - this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant2TextFill || adjust(this.primaryTextColor, { r: -5, g: -5, b: -5 }); this.quadrant3TextFill = - this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant3TextFill || adjust(this.primaryTextColor, { r: -10, g: -10, b: -10 }); this.quadrant4TextFill = - this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrant4TextFill || adjust(this.primaryTextColor, { r: -15, g: -15, b: -15 }); this.quadrantPointFill = this.quadrantPointFill || isDark(this.quadrant1Fill) ? lighten(this.quadrant1Fill) @@ -242,7 +242,7 @@ class Theme { this.quadrantInternalBorderStrokeFill = this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; this.quadrantExternalBorderStrokeFill = - this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantExternalBorderStrokeFill || this.primaryBorderColor; this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ diff --git a/packages/mermaid/src/themes/theme-dark.js b/packages/mermaid/src/themes/theme-dark.js index b05308d2e..fd083e513 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -228,16 +228,16 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.primaryColor, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.primaryColor, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; this.quadrant2TextFill = - this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant2TextFill || adjust(this.primaryTextColor, { r: -5, g: -5, b: -5 }); this.quadrant3TextFill = - this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant3TextFill || adjust(this.primaryTextColor, { r: -10, g: -10, b: -10 }); this.quadrant4TextFill = - this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrant4TextFill || adjust(this.primaryTextColor, { r: -15, g: -15, b: -15 }); this.quadrantPointFill = this.quadrantPointFill || isDark(this.quadrant1Fill) ? lighten(this.quadrant1Fill) @@ -248,7 +248,7 @@ class Theme { this.quadrantInternalBorderStrokeFill = this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; this.quadrantExternalBorderStrokeFill = - this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantExternalBorderStrokeFill || this.primaryBorderColor; this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* class */ diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index 326338aee..3cd6bca4f 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -249,16 +249,16 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.primaryColor, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.primaryColor, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; this.quadrant2TextFill = - this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant2TextFill || adjust(this.primaryTextColor, { r: -5, g: -5, b: -5 }); this.quadrant3TextFill = - this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant3TextFill || adjust(this.primaryTextColor, { r: -10, g: -10, b: -10 }); this.quadrant4TextFill = - this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrant4TextFill || adjust(this.primaryTextColor, { r: -15, g: -15, b: -15 }); this.quadrantPointFill = this.quadrantPointFill || isDark(this.quadrant1Fill) ? lighten(this.quadrant1Fill) @@ -269,7 +269,7 @@ class Theme { this.quadrantInternalBorderStrokeFill = this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; this.quadrantExternalBorderStrokeFill = - this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantExternalBorderStrokeFill || this.primaryBorderColor; this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ diff --git a/packages/mermaid/src/themes/theme-forest.js b/packages/mermaid/src/themes/theme-forest.js index 9dda643ff..65797b00c 100644 --- a/packages/mermaid/src/themes/theme-forest.js +++ b/packages/mermaid/src/themes/theme-forest.js @@ -217,16 +217,16 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.primaryColor, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.primaryColor, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; this.quadrant2TextFill = - this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant2TextFill || adjust(this.primaryTextColor, { r: -5, g: -5, b: -5 }); this.quadrant3TextFill = - this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant3TextFill || adjust(this.primaryTextColor, { r: -10, g: -10, b: -10 }); this.quadrant4TextFill = - this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrant4TextFill || adjust(this.primaryTextColor, { r: -15, g: -15, b: -15 }); this.quadrantPointFill = this.quadrantPointFill || isDark(this.quadrant1Fill) ? lighten(this.quadrant1Fill) @@ -237,7 +237,7 @@ class Theme { this.quadrantInternalBorderStrokeFill = this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; this.quadrantExternalBorderStrokeFill = - this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantExternalBorderStrokeFill || this.primaryBorderColor; this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index 43d0b61b5..963ce031d 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -248,16 +248,16 @@ class Theme { /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || this.primaryColor; - this.quadrant2Fill = this.quadrant2Fill || adjust(this.quadrant1Fill, { r: 5, g: 5, b: 5 }); - this.quadrant3Fill = this.quadrant3Fill || adjust(this.quadrant1Fill, { r: 10, g: 10, b: 10 }); - this.quadrant4Fill = this.quadrant4Fill || adjust(this.quadrant1Fill, { r: 15, g: 15, b: 15 }); + this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 }); + this.quadrant3Fill = this.quadrant3Fill || adjust(this.primaryColor, { r: 10, g: 10, b: 10 }); + this.quadrant4Fill = this.quadrant4Fill || adjust(this.primaryColor, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; this.quadrant2TextFill = - this.quadrant2TextFill || adjust(this.quadrant1TextFill, { r: -5, g: -5, b: -5 }); + this.quadrant2TextFill || adjust(this.primaryTextColor, { r: -5, g: -5, b: -5 }); this.quadrant3TextFill = - this.quadrant3TextFill || adjust(this.quadrant1TextFill, { r: -10, g: -10, b: -10 }); + this.quadrant3TextFill || adjust(this.primaryTextColor, { r: -10, g: -10, b: -10 }); this.quadrant4TextFill = - this.quadrant4TextFill || adjust(this.quadrant1TextFill, { r: -15, g: -15, b: -15 }); + this.quadrant4TextFill || adjust(this.primaryTextColor, { r: -15, g: -15, b: -15 }); this.quadrantPointFill = this.quadrantPointFill || isDark(this.quadrant1Fill) ? lighten(this.quadrant1Fill) @@ -268,7 +268,7 @@ class Theme { this.quadrantInternalBorderStrokeFill = this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; this.quadrantExternalBorderStrokeFill = - this.quadrantExternalBorderStrokeFill || this.secondaryBorderColor; + this.quadrantExternalBorderStrokeFill || this.primaryBorderColor; this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* requirement-diagram */ From b2a5ddcd85974b689ae0cf1bc2fb87672762d703 Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Sat, 13 May 2023 17:33:31 +0530 Subject: [PATCH 7/9] Added documentation for the quadrantChart --- docs/intro/index.md | 36 ++++ docs/syntax/quadrantChart.md | 171 ++++++++++++++++++ packages/mermaid/src/defaultConfig.ts | 154 ++++++++-------- .../quadrant-chart/quadrantBuilder.ts | 2 +- packages/mermaid/src/docs/intro/examples.md | 19 ++ .../mermaid/src/docs/syntax/quadrantChart.md | 142 +++++++++++++++ 6 files changed, 446 insertions(+), 78 deletions(-) create mode 100644 docs/syntax/quadrantChart.md create mode 100644 packages/mermaid/src/docs/syntax/quadrantChart.md diff --git a/docs/intro/index.md b/docs/intro/index.md index 8ebc4c530..36707bace 100644 --- a/docs/intro/index.md +++ b/docs/intro/index.md @@ -235,6 +235,42 @@ journey Sit down: 5: Me ``` +### [Quadrant Chart](../syntax/quadrantChart.md) + +```mermaid-example +quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] +``` + +```mermaid +quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] +``` + ## Installation **In depth guides and examples can be found at [Getting Started](./n00b-gettingStarted.md) and [Usage](../config/usage.md).** diff --git a/docs/syntax/quadrantChart.md b/docs/syntax/quadrantChart.md new file mode 100644 index 000000000..86aa21f72 --- /dev/null +++ b/docs/syntax/quadrantChart.md @@ -0,0 +1,171 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/quadrantChart.md](../../packages/mermaid/src/docs/syntax/quadrantChart.md). + +# Quadrant Chart + +> A quadrant chart is a visual representation of data that is divided into four quadrants. It is used to plot data points on a two-dimensional grid, with one variable represented on the x-axis and another variable represented on the y-axis. The quadrants are determined by dividing the chart into four equal parts based on a set of criteria that is specific to the data being analyzed. Quadrant charts are often used to identify patterns and trends in data, and to prioritize actions based on the position of data points within the chart. They are commonly used in business, marketing, and risk management, among other fields. + +## Example + +```mermaid-example +quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] +``` + +```mermaid +quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] +``` + +## Syntax + +> **Note** +> In place of `<text>` you can use text like `this is a sample text` or inside **double quotes** like `"This type of text may contain unicode like ❤"`. + +> **Note** +> If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant. +> If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant. + +> **Note** +> For points x and y value min value is 0 and max value is 1. + +### Title + +The title is a short description of the chart and it will always render on top of the chart. + +#### Example + + quadrantChart + title This is a sample example + +### x-axis + +The x-axis determine what text would be displayed in the x-axis. In x-axis there is two part **left** and **right** you can pass **both** or you can pass only **left**. The statement should start with `x-axis` then the `left axis text` followed by the delimiter `-->` then `right axis text`. + +#### Example + +1. `x-axis <text> --> <text>` both the left and right axis text will be rendered. +2. `x-axis <text>` only the left axis text will be rendered. + +### y-axis + +The y-axis determine what text would be displayed in the y-axis. In y-axis there is two part **top** and **bottom** you can pass **both** or you can pass only **bottom**. The statement should start with `y-axis` then the `bottom axis text` followed by the delimiter `-->` then `top axis text`. + +#### Example + +1. `y-axis <text> --> <text>` both the bottom and top axis text will be rendered. +2. `y-axis <text>` only the bottom axis text will be rendered. + +### Quadrants text + +The `quadrant-[1,2,3,4]` determine what text would be displayed inside the quadrants. + +#### Example + +1. `quadrant-1 <text>` determine what text will be rendered inside the top right quadrant. +2. `quadrant-2 <text>` determine what text will be rendered inside the top left quadrant. +3. `quadrant-3 <text>` determine what text will be rendered inside the bottom left quadrant. +4. `quadrant-4 <text>` determine what text will be rendered inside the bottom right quadrant. + +### Points + +Points are used to plot a circle inside the quadrantChart. The syntax is `<text>: [x, y]` here x and y value is in the range 0 - 1. + +#### Example + +1. `Point 1: [0.75, 0.80]` here the Point 1 will be drawn in the top right quadrant. +2. `Point 2: [0.35, 0.24]` here the Point 2 will be drawn in the bottom left quadrant. + +## Chart Configurations + +| Parameter | Description | Default value | +| --------------------------------- | ------------------------------------------------------------------------------------------------- | :-----------: | +| chartWidth | Width of the chart | 500 | +| chartHeight | Height of the chart | 500 | +| titlePadding | Top and Bottom padding of the title | 10 | +| titleFontSize | Title font size | 20 | +| quadrantPadding | Padding outside all the quadrants | 5 | +| quadrantTextTopPadding | Quadrant text top padding when text is drawn on top ( not data points are there) | 5 | +| quadrantLabelFontSize | Quadrant text font size | 16 | +| quadrantInternalBorderStrokeWidth | Border stroke width inside the quadrants | 1 | +| quadrantExternalBorderStrokeWidth | Quadrant external border stroke width | 2 | +| xAxisLabelPadding | Top and bottom padding of x-axis text | 5 | +| xAxisLabelFontSize | X-axis texts font size | 16 | +| xAxisPosition | Position of x-axis (top , bottom) if there are points the x-axis will alway be rendered in bottom | 'top' | +| yAxisLabelPadding | Left and Right padding of y-axis text | 5 | +| yAxisLabelFontSize | Y-axis texts font size | 16 | +| yAxisPosition | Position of y-axis (left , right) | 'left' | +| pointTextPadding | Padding between point and the below text | 5 | +| pointLabelFontSize | Point text font size | 12 | +| pointRadius | Radius of the point to be drawn | 5 | + +## Chart Theme Variables + +| Parameter | Description | +| -------------------------------- | --------------------------------------- | +| quadrant1Fill | Fill color of the top right quadrant | +| quadrant2Fill | Fill color of the top left quadrant | +| quadrant3Fill | Fill color of the bottom left quadrant | +| quadrant4Fill | Fill color of the bottom right quadrant | +| quadrant1TextFill | Text color of the top right quadrant | +| quadrant2TextFill | Text color of the top left quadrant | +| quadrant3TextFill | Text color of the bottom left quadrant | +| quadrant4TextFill | Text color of the bottom right quadrant | +| quadrantPointFill | Points fill color | +| quadrantPointTextFill | Points text color | +| quadrantXAxisTextFill | X-axis text color | +| quadrantYAxisTextFill | Y-axis text color | +| quadrantInternalBorderStrokeFill | Quadrants inner border color | +| quadrantExternalBorderStrokeFill | Quadrants outer border color | +| quadrantTitleFill | Title color | + +## Example on config and theme + +```mermaid-example +%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%% +quadrantChart + x-axis Urgent --> Not Urgent + y-axis Not Important --> important + quadrant-1 Plan + quadrant-2 Do + quadrant-3 Deligate + quadrant-4 Delete +``` + +```mermaid +%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%% +quadrantChart + x-axis Urgent --> Not Urgent + y-axis Not Important --> important + quadrant-1 Plan + quadrant-2 Do + quadrant-3 Deligate + quadrant-4 Delete +``` diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index ade873e57..8f715cb26 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -1299,6 +1299,24 @@ const config: Partial<MermaidConfig> = { * Default value: 500 */ chartHeight: 500, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | titlePadding | Chart title top and bottom padding | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 10 + */ + titlePadding: 10, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | titleFontSize | Chart title font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 20 + */ + titleFontSize: 20, /** * | Parameter | Description | Type | Required | Values | * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | @@ -1308,6 +1326,42 @@ const config: Partial<MermaidConfig> = { * Default value: 5 */ quadrantPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ---------------------- | -------------------------------------------------------------------------- | ------- | -------- | ------------------- | + * | quadrantTextTopPadding | quadrant title padding from top if the quadrant is rendered on top | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 5 + */ + quadrantTextTopPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | quadrantLabelFontSize | quadrant title font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 16 + */ + quadrantLabelFontSize: 16, + /** + * | Parameter | Description | Type | Required | Values | + * | --------------------------------- | ------------------------------------------------------------- | ------- | -------- | ------------------- | + * | quadrantInternalBorderStrokeWidth | stroke width of edges of the box that are inside the quadrant | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 1 + */ + quadrantInternalBorderStrokeWidth: 1, + /** + * | Parameter | Description | Type | Required | Values | + * | --------------------------------- | -------------------------------------------------------------- | ------- | -------- | ------------------- | + * | quadrantExternalBorderStrokeWidth | stroke width of edges of the box that are outside the quadrant | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 2 + */ + quadrantExternalBorderStrokeWidth: 2, /** * | Parameter | Description | Type | Required | Values | * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | @@ -1317,6 +1371,24 @@ const config: Partial<MermaidConfig> = { * Default value: 5 */ xAxisLabelPadding: 5, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | + * | xAxisLabelFontSize | x-axis label font size | number | Optional | Any positive number | + * + * **Notes:** + * Default value: 16 + */ + xAxisLabelFontSize: 16, + /** + * | Parameter | Description | Type | Required | Values | + * | ------------- | ------------------------------- | ------- | -------- | ------------------- | + * | xAxisPosition | position of x-axis labels | string | Optional | 'top' or 'bottom' | + * + * **Notes:** + * Default value: top + */ + xAxisPosition: 'top', /** * | Parameter | Description | Type | Required | Values | * | --------------- | ---------------------------------- | ------- | -------- | ------------------- | @@ -1326,33 +1398,6 @@ const config: Partial<MermaidConfig> = { * Default value: 5 */ yAxisLabelPadding: 5, - /** - * | Parameter | Description | Type | Required | Values | - * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | - * | titlePadding | Chart title top and bottom padding | number | Optional | Any positive number | - * - * **Notes:** - * Default value: 5 - */ - titlePadding: 5, - /** - * | Parameter | Description | Type | Required | Values | - * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | - * | titleFontSize | Chart title font size | number | Optional | Any positive number | - * - * **Notes:** - * Default value: 20 - */ - titleFontSize: 20, - /** - * | Parameter | Description | Type | Required | Values | - * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | - * | xAxisLabelFontSize | x-axis label font size | number | Optional | Any positive number | - * - * **Notes:** - * Default value: 16 - */ - xAxisLabelFontSize: 16, /** * | Parameter | Description | Type | Required | Values | * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | @@ -1363,23 +1408,14 @@ const config: Partial<MermaidConfig> = { */ yAxisLabelFontSize: 16, /** - * | Parameter | Description | Type | Required | Values | - * | ------------------ | ---------------------------------- | ------- | -------- | ------------------- | - * | quadrantLabelFontSize | quadrant title font size | number | Optional | Any positive number | + * | Parameter | Description | Type | Required | Values | + * | ------------- | ------------------------------- | ------- | -------- | ------------------- | + * | yAxisPosition | position of y-axis labels | string | Optional | 'left' or 'right' | * * **Notes:** - * Default value: 16 + * Default value: left */ - quadrantLabelFontSize: 16, - /** - * | Parameter | Description | Type | Required | Values | - * | ---------------------- | -------------------------------------------------------------------------- | ------- | -------- | ------------------- | - * | quadrantTextTopPadding | quadrant title padding from top if the quadrant is rendered on top | number | Optional | Any positive number | - * - * **Notes:** - * Default value: 5 - */ - quadrantTextTopPadding: 5, + yAxisPosition: 'left', /** * | Parameter | Description | Type | Required | Values | * | ---------------------- | -------------------------------------- | ------- | -------- | ------------------- | @@ -1407,42 +1443,6 @@ const config: Partial<MermaidConfig> = { * Default value: 5 */ pointRadius: 5, - /** - * | Parameter | Description | Type | Required | Values | - * | ------------- | ------------------------------- | ------- | -------- | ------------------- | - * | xAxisPosition | position of x-axis labels | string | Optional | 'top' or 'bottom' | - * - * **Notes:** - * Default value: top - */ - xAxisPosition: 'top', - /** - * | Parameter | Description | Type | Required | Values | - * | ------------- | ------------------------------- | ------- | -------- | ------------------- | - * | yAxisPosition | position of y-axis labels | string | Optional | 'left' or 'right' | - * - * **Notes:** - * Default value: left - */ - yAxisPosition: 'left', - /** - * | Parameter | Description | Type | Required | Values | - * | --------------------------------- | ------------------------------------------------------------- | ------- | -------- | ------------------- | - * | quadrantInternalBorderStrokeWidth | stroke width of edges of the box that are inside the quadrant | number | Optional | Any positive number | - * - * **Notes:** - * Default value: 1 - */ - quadrantInternalBorderStrokeWidth: 1, - /** - * | Parameter | Description | Type | Required | Values | - * | --------------------------------- | -------------------------------------------------------------- | ------- | -------- | ------------------- | - * | quadrantExternalBorderStrokeWidth | stroke width of edges of the box that are outside the quadrant | number | Optional | Any positive number | - * - * **Notes:** - * Default value: 2 - */ - quadrantExternalBorderStrokeWidth: 2, /** * | Parameter | Description | Type | Required | Values | * | ----------- | ----------- | ------- | -------- | ----------- | diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts index 95b8c6679..dc23a55c2 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts @@ -150,7 +150,7 @@ export class QuadrantBuilder { showTitle: true, chartHeight: defaultConfig.quadrantChart?.chartWidth || 500, chartWidth: defaultConfig.quadrantChart?.chartHeight || 500, - titlePadding: defaultConfig.quadrantChart?.titlePadding || 5, + titlePadding: defaultConfig.quadrantChart?.titlePadding || 10, titleFontSize: defaultConfig.quadrantChart?.titleFontSize || 20, quadrantPadding: defaultConfig.quadrantChart?.quadrantPadding || 5, xAxisLabelPadding: defaultConfig.quadrantChart?.xAxisLabelPadding || 5, diff --git a/packages/mermaid/src/docs/intro/examples.md b/packages/mermaid/src/docs/intro/examples.md index f4cb3b929..7dda288dc 100644 --- a/packages/mermaid/src/docs/intro/examples.md +++ b/packages/mermaid/src/docs/intro/examples.md @@ -98,3 +98,22 @@ journey Go downstairs: 5: Me Sit down: 5: Me ``` + +### [Quadrant Chart](../syntax/quadrantChart.md) + +```mermaid-example +quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] +``` diff --git a/packages/mermaid/src/docs/syntax/quadrantChart.md b/packages/mermaid/src/docs/syntax/quadrantChart.md new file mode 100644 index 000000000..351dd1ad1 --- /dev/null +++ b/packages/mermaid/src/docs/syntax/quadrantChart.md @@ -0,0 +1,142 @@ +# Quadrant Chart + +> A quadrant chart is a visual representation of data that is divided into four quadrants. It is used to plot data points on a two-dimensional grid, with one variable represented on the x-axis and another variable represented on the y-axis. The quadrants are determined by dividing the chart into four equal parts based on a set of criteria that is specific to the data being analyzed. Quadrant charts are often used to identify patterns and trends in data, and to prioritize actions based on the position of data points within the chart. They are commonly used in business, marketing, and risk management, among other fields. + +## Example + +```mermaid-example +quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] +``` + +## Syntax + +```note +In place of `<text>` you can use text like `this is a sample text` or inside **double quotes** like `"This type of text may contain unicode like ❤"`. +``` + +```note +If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant. +If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant. +``` + +```note +For points x and y value min value is 0 and max value is 1. +``` + +### Title + +The title is a short description of the chart and it will always render on top of the chart. + +#### Example + +``` +quadrantChart + title This is a sample example +``` + +### x-axis + +The x-axis determine what text would be displayed in the x-axis. In x-axis there is two part **left** and **right** you can pass **both** or you can pass only **left**. The statement should start with `x-axis` then the `left axis text` followed by the delimiter `-->` then `right axis text`. + +#### Example + +1. `x-axis <text> --> <text>` both the left and right axis text will be rendered. +2. `x-axis <text>` only the left axis text will be rendered. + +### y-axis + +The y-axis determine what text would be displayed in the y-axis. In y-axis there is two part **top** and **bottom** you can pass **both** or you can pass only **bottom**. The statement should start with `y-axis` then the `bottom axis text` followed by the delimiter `-->` then `top axis text`. + +#### Example + +1. `y-axis <text> --> <text>` both the bottom and top axis text will be rendered. +2. `y-axis <text>` only the bottom axis text will be rendered. + +### Quadrants text + +The `quadrant-[1,2,3,4]` determine what text would be displayed inside the quadrants. + +#### Example + +1. `quadrant-1 <text>` determine what text will be rendered inside the top right quadrant. +2. `quadrant-2 <text>` determine what text will be rendered inside the top left quadrant. +3. `quadrant-3 <text>` determine what text will be rendered inside the bottom left quadrant. +4. `quadrant-4 <text>` determine what text will be rendered inside the bottom right quadrant. + +### Points + +Points are used to plot a circle inside the quadrantChart. The syntax is `<text>: [x, y]` here x and y value is in the range 0 - 1. + +#### Example + +1. `Point 1: [0.75, 0.80]` here the Point 1 will be drawn in the top right quadrant. +2. `Point 2: [0.35, 0.24]` here the Point 2 will be drawn in the bottom left quadrant. + +## Chart Configurations + +| Parameter | Description | Default value | +| --------------------------------- | ------------------------------------------------------------------------------------------------- | :-----------: | +| chartWidth | Width of the chart | 500 | +| chartHeight | Height of the chart | 500 | +| titlePadding | Top and Bottom padding of the title | 10 | +| titleFontSize | Title font size | 20 | +| quadrantPadding | Padding outside all the quadrants | 5 | +| quadrantTextTopPadding | Quadrant text top padding when text is drawn on top ( not data points are there) | 5 | +| quadrantLabelFontSize | Quadrant text font size | 16 | +| quadrantInternalBorderStrokeWidth | Border stroke width inside the quadrants | 1 | +| quadrantExternalBorderStrokeWidth | Quadrant external border stroke width | 2 | +| xAxisLabelPadding | Top and bottom padding of x-axis text | 5 | +| xAxisLabelFontSize | X-axis texts font size | 16 | +| xAxisPosition | Position of x-axis (top , bottom) if there are points the x-axis will alway be rendered in bottom | 'top' | +| yAxisLabelPadding | Left and Right padding of y-axis text | 5 | +| yAxisLabelFontSize | Y-axis texts font size | 16 | +| yAxisPosition | Position of y-axis (left , right) | 'left' | +| pointTextPadding | Padding between point and the below text | 5 | +| pointLabelFontSize | Point text font size | 12 | +| pointRadius | Radius of the point to be drawn | 5 | + +## Chart Theme Variables + +| Parameter | Description | +| -------------------------------- | --------------------------------------- | +| quadrant1Fill | Fill color of the top right quadrant | +| quadrant2Fill | Fill color of the top left quadrant | +| quadrant3Fill | Fill color of the bottom left quadrant | +| quadrant4Fill | Fill color of the bottom right quadrant | +| quadrant1TextFill | Text color of the top right quadrant | +| quadrant2TextFill | Text color of the top left quadrant | +| quadrant3TextFill | Text color of the bottom left quadrant | +| quadrant4TextFill | Text color of the bottom right quadrant | +| quadrantPointFill | Points fill color | +| quadrantPointTextFill | Points text color | +| quadrantXAxisTextFill | X-axis text color | +| quadrantYAxisTextFill | Y-axis text color | +| quadrantInternalBorderStrokeFill | Quadrants inner border color | +| quadrantExternalBorderStrokeFill | Quadrants outer border color | +| quadrantTitleFill | Title color | + +## Example on config and theme + +```mermaid-example +%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%% +quadrantChart + x-axis Urgent --> Not Urgent + y-axis Not Important --> important + quadrant-1 Plan + quadrant-2 Do + quadrant-3 Deligate + quadrant-4 Delete +``` From 0f354ff9747dbab1bca8e3021355a4e19c20b58c Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Sat, 13 May 2023 19:45:45 +0530 Subject: [PATCH 8/9] Added e2e test cases with some fixes --- .../rendering/quadrantChart.spec.js | 163 ++++++++++++++++++ .../quadrant-chart/quadrantBuilder.ts | 20 ++- .../quadrant-chart/quadrantRenderer.ts | 2 +- 3 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 cypress/integration/rendering/quadrantChart.spec.js diff --git a/cypress/integration/rendering/quadrantChart.spec.js b/cypress/integration/rendering/quadrantChart.spec.js new file mode 100644 index 000000000..4bcf58b60 --- /dev/null +++ b/cypress/integration/rendering/quadrantChart.spec.js @@ -0,0 +1,163 @@ +import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; + +describe('Quadrant Chart', () => { + it('should render if only chart type is provided', () => { + imgSnapshotTest( + ` + quadrantChart + `, + {} + ); + cy.get('svg'); + }); + it('should render a complete quadrant chart', () => { + imgSnapshotTest( + ` + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] + `, + {} + ); + cy.get('svg'); + }); + it('should render without points', () => { + imgSnapshotTest( + ` + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + `, + {} + ); + cy.get('svg'); + }); + it('should able to render y-axix on right side', () => { + imgSnapshotTest( + ` + %%{init: {"quadrantChart": {"yAxisPosition": "right"}}}%% + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + `, + {} + ); + cy.get('svg'); + }); + it('should able to render x-axix on bottom', () => { + imgSnapshotTest( + ` + %%{init: {"quadrantChart": {"xAxisPosition": "bottom"}}}%% + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + `, + {} + ); + cy.get('svg'); + }); + it('should able to render x-axix on bottom and y-axis on right', () => { + imgSnapshotTest( + ` + %%{init: {"quadrantChart": {"xAxisPosition": "bottom", "yAxisPosition": "right"}}}%% + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + `, + {} + ); + cy.get('svg'); + }); + it('should render without title', () => { + imgSnapshotTest( + ` + quadrantChart + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + `, + {} + ); + cy.get('svg'); + }); + it('should use all the config', () => { + imgSnapshotTest( + ` + %%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600, "titlePadding": 20, "titleFontSize": 10, "quadrantPadding": 20, "quadrantTextTopPadding": 40, "quadrantLabelFontSize": 20, "quadrantInternalBorderStrokeWidth": 3, "quadrantExternalBorderStrokeWidth": 5, "xAxisLabelPadding": 20, "xAxisLabelFontSize": 20, "yAxisLabelPadding": 20, "yAxisLabelFontSize": 20, "pointTextPadding": 20, "pointLabelFontSize": 20, "pointRadius": 10 }}}%% + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] + `, + {} + ); + cy.get('svg'); + }); + it('should use all the theme variable', () => { + imgSnapshotTest( + ` + %%{init: {"themeVariables": {"quadrant1Fill": "#b4dcff","quadrant2Fill": "#fef0ff", "quadrant3Fill": "#fffaf0", "quadrant4Fill": "#f0fff2", "quadrant1TextFill": "#ff0000", "quadrant2TextFill": "#2d00df", "quadrant3TextFill": "#00ffda", "quadrant4TextFill": "#e68300", "quadrantPointFill": "#0149ff", "quadrantPointTextFill": "#dc00ff", "quadrantXAxisTextFill": "#ffb500", "quadrantYAxisTextFill": "#fae604", "quadrantInternalBorderStrokeFill": "#3636f2", "quadrantExternalBorderStrokeFill": "#ff1010", "quadrantTitleFill": "#00ea19"} }}%% + quadrantChart + title Reach and engagement of campaigns + x-axis Low Reach --> High Reach + y-axis Low Engagement --> High Engagement + quadrant-1 We should expand + quadrant-2 Need to promote + quadrant-3 Re-evaluate + quadrant-4 May be improved + Campaign A: [0.3, 0.6] + Campaign B: [0.45, 0.23] + Campaign C: [0.57, 0.69] + Campaign D: [0.78, 0.34] + Campaign E: [0.40, 0.34] + Campaign F: [0.35, 0.78] + `, + {} + ); + cy.get('svg'); + }); +}); diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts index dc23a55c2..8168551ad 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts @@ -297,7 +297,10 @@ export class QuadrantBuilder { y: xAxisPosition === 'top' ? this.config.xAxisLabelPadding + titleSpace.top - : this.config.xAxisLabelPadding + quadrantTop + quadrantHeight, + : this.config.xAxisLabelPadding + + quadrantTop + + quadrantHeight + + this.config.quadrantPadding, fontSize: this.config.xAxisLabelFontSize, verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', horizontalPos: 'top', @@ -312,7 +315,10 @@ export class QuadrantBuilder { y: xAxisPosition === 'top' ? this.config.xAxisLabelPadding + titleSpace.top - : this.config.xAxisLabelPadding + quadrantTop + quadrantHeight, + : this.config.xAxisLabelPadding + + quadrantTop + + quadrantHeight + + this.config.quadrantPadding, fontSize: this.config.xAxisLabelFontSize, verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', horizontalPos: 'top', @@ -327,7 +333,10 @@ export class QuadrantBuilder { x: this.config.yAxisPosition === 'left' ? this.config.yAxisLabelPadding - : this.config.yAxisLabelPadding + quadrantLeft + quadrantWidth, + : this.config.yAxisLabelPadding + + quadrantLeft + + quadrantWidth + + this.config.quadrantPadding, y: quadrantTop + quadrantHeight - (drawAxisLabelInMiddle ? quadrantHalfHeight / 2 : 0), fontSize: this.config.yAxisLabelFontSize, verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', @@ -342,7 +351,10 @@ export class QuadrantBuilder { x: this.config.yAxisPosition === 'left' ? this.config.yAxisLabelPadding - : this.config.yAxisLabelPadding + quadrantLeft + quadrantWidth, + : this.config.yAxisLabelPadding + + quadrantLeft + + quadrantWidth + + this.config.quadrantPadding, y: quadrantTop + quadrantHalfHeight - (drawAxisLabelInMiddle ? quadrantHalfHeight / 2 : 0), fontSize: this.config.yAxisLabelFontSize, verticalPos: drawAxisLabelInMiddle ? 'center' : 'left', diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts index 955f77f54..46d3f773b 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts @@ -16,7 +16,7 @@ import { export const draw = (txt: string, id: string, _version: string, diagObj: Diagram) => { function getDominantBaseLine(horizontalPos: TextHorizontalPos) { - return horizontalPos === 'top' ? 'text-before-edge' : 'middle'; + return horizontalPos === 'top' ? 'hanging' : 'middle'; } function getTextAnchor(verticalPos: TextVerticalPos) { From c4aebe6d579e5c8c1b1fad21a4806f71060ea4c6 Mon Sep 17 00:00:00 2001 From: Subhash Halder <halder.subhash@gmail.com> Date: Sat, 13 May 2023 20:28:49 +0530 Subject: [PATCH 9/9] Added quadrantChart to the side bar --- packages/mermaid/src/docs/.vitepress/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 2ca53b348..7ade4bd3f 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -115,6 +115,7 @@ function sidebarSyntax() { { text: 'User Journey', link: '/syntax/userJourney' }, { text: 'Gantt', link: '/syntax/gantt' }, { text: 'Pie Chart', link: '/syntax/pie' }, + { text: 'Quadrant Chart', link: '/syntax/quadrantChart' }, { text: 'Requirement Diagram', link: '/syntax/requirementDiagram' }, { text: 'Gitgraph (Git) Diagram 🔥', link: '/syntax/gitgraph' }, { text: 'C4C Diagram (Context) Diagram 🦺⚠️', link: '/syntax/c4c' },