mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-25 10:20:06 +02:00
Merge branch 'develop' into feature/1295_generic_rendering_engine
This commit is contained in:
@@ -154,6 +154,28 @@ function stadium(parent, bbox, node) {
|
||||
return shapeSvg;
|
||||
}
|
||||
|
||||
function subroutine(parent, bbox, node) {
|
||||
const w = bbox.width;
|
||||
const h = bbox.height;
|
||||
const points = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: w, y: 0 },
|
||||
{ x: w, y: -h },
|
||||
{ x: 0, y: -h },
|
||||
{ x: 0, y: 0 },
|
||||
{ x: -8, y: 0 },
|
||||
{ x: w + 8, y: 0 },
|
||||
{ x: w + 8, y: -h },
|
||||
{ x: -8, y: -h },
|
||||
{ x: -8, y: 0 }
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function(point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
|
||||
function cylinder(parent, bbox, node) {
|
||||
const w = bbox.width;
|
||||
const rx = w / 2;
|
||||
@@ -221,6 +243,7 @@ export function addToRender(render) {
|
||||
render.shapes().question = question;
|
||||
render.shapes().hexagon = hexagon;
|
||||
render.shapes().stadium = stadium;
|
||||
render.shapes().subroutine = subroutine;
|
||||
render.shapes().cylinder = cylinder;
|
||||
|
||||
// Add custom shape for box with inverted arrow on left side
|
||||
@@ -246,6 +269,7 @@ export function addToRenderV2(addShape) {
|
||||
addShape({ question });
|
||||
addShape({ hexagon });
|
||||
addShape({ stadium });
|
||||
addShape({ subroutine });
|
||||
addShape({ cylinder });
|
||||
|
||||
// Add custom shape for box with inverted arrow on left side
|
||||
|
@@ -65,7 +65,8 @@ describe('flowchart shapes', function() {
|
||||
['lean_right', 4, useWidth, useHeight],
|
||||
['lean_left', 4, useWidth, useHeight],
|
||||
['trapezoid', 4, useWidth, useHeight],
|
||||
['inv_trapezoid', 4, useWidth, useHeight]
|
||||
['inv_trapezoid', 4, useWidth, useHeight],
|
||||
['subroutine', 10, useWidth, useHeight],
|
||||
].forEach(function([shapeType, expectedPointCount, getW, getH]) {
|
||||
it(`should add a ${shapeType} shape that renders a properly translated polygon element`, function() {
|
||||
const mockRender = MockRender();
|
||||
|
@@ -119,6 +119,9 @@ export const addVertices = function(vert, g, svgId) {
|
||||
case 'stadium':
|
||||
_shape = 'stadium';
|
||||
break;
|
||||
case 'subroutine':
|
||||
_shape = 'subroutine';
|
||||
break;
|
||||
case 'cylinder':
|
||||
_shape = 'cylinder';
|
||||
break;
|
||||
|
@@ -119,6 +119,9 @@ export const addVertices = function(vert, g, svgId) {
|
||||
case 'stadium':
|
||||
_shape = 'stadium';
|
||||
break;
|
||||
case 'subroutine':
|
||||
_shape = 'subroutine';
|
||||
break;
|
||||
case 'cylinder':
|
||||
_shape = 'cylinder';
|
||||
break;
|
||||
@@ -380,10 +383,6 @@ export const draw = function(text, id) {
|
||||
const svgBounds = svg.node().getBBox();
|
||||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
logger.debug(
|
||||
`new ViewBox 0 0 ${width} ${height}`,
|
||||
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
|
||||
);
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
svg.attr('width', '100%');
|
||||
@@ -393,10 +392,10 @@ export const draw = function(text, id) {
|
||||
svg.attr('width', width);
|
||||
}
|
||||
|
||||
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
svg
|
||||
.select('g')
|
||||
.attr('transform', `translate(${padding - g._label.marginx}, ${padding - svgBounds.y})`);
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
logger.debug(`viewBox ${vBox}`);
|
||||
svg.attr('viewBox', vBox);
|
||||
|
||||
// Index nodes
|
||||
flowDb.indexNodes('subGraph' + i);
|
||||
|
@@ -23,6 +23,7 @@ describe('the flowchart renderer', function() {
|
||||
['circle', 'circle'],
|
||||
['ellipse', 'ellipse'],
|
||||
['stadium', 'stadium'],
|
||||
['subroutine', 'subroutine'],
|
||||
['cylinder', 'cylinder'],
|
||||
['group', 'rect']
|
||||
].forEach(function([type, expectedShape, expectedRadios = 0]) {
|
||||
|
@@ -87,6 +87,8 @@
|
||||
"-)" return '-)';
|
||||
"([" return 'STADIUMSTART';
|
||||
"])" return 'STADIUMEND';
|
||||
"[[" return 'SUBROUTINESTART';
|
||||
"]]" return 'SUBROUTINEEND';
|
||||
"[(" return 'CYLINDERSTART';
|
||||
")]" return 'CYLINDEREND';
|
||||
\- return 'MINUS';
|
||||
@@ -316,6 +318,8 @@ vertex: idString SQS text SQE
|
||||
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
||||
| idString STADIUMSTART text STADIUMEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'stadium');}
|
||||
| idString SUBROUTINESTART text SUBROUTINEEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'subroutine');}
|
||||
| idString CYLINDERSTART text CYLINDEREND
|
||||
{$$ = $1;yy.addVertex($1,$3,'cylinder');}
|
||||
| idString PS text PE
|
||||
@@ -474,5 +478,5 @@ alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA |
|
||||
|
||||
idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION | AMP;
|
||||
|
||||
graphCodeTokens: STADIUMSTART | STADIUMEND | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI;
|
||||
graphCodeTokens: STADIUMSTART | STADIUMEND | SUBROUTINESTART | SUBROUTINEEND | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI;
|
||||
%%
|
||||
|
@@ -6,6 +6,7 @@ import { getConfig } from '../../config';
|
||||
const config = getConfig();
|
||||
let dateFormat = '';
|
||||
let axisFormat = '';
|
||||
let todayMarker = '';
|
||||
let excludes = [];
|
||||
let title = '';
|
||||
let sections = [];
|
||||
@@ -27,6 +28,7 @@ export const clear = function() {
|
||||
rawTasks = [];
|
||||
dateFormat = '';
|
||||
axisFormat = '';
|
||||
todayMarker = '';
|
||||
excludes = [];
|
||||
inclusiveEndDates = false;
|
||||
};
|
||||
@@ -39,6 +41,14 @@ export const getAxisFormat = function() {
|
||||
return axisFormat;
|
||||
};
|
||||
|
||||
export const setTodayMarker = function(txt) {
|
||||
todayMarker = txt;
|
||||
};
|
||||
|
||||
export const getTodayMarker = function() {
|
||||
return todayMarker;
|
||||
};
|
||||
|
||||
export const setDateFormat = function(txt) {
|
||||
dateFormat = txt;
|
||||
};
|
||||
@@ -572,6 +582,8 @@ export default {
|
||||
endDatesAreInclusive,
|
||||
setAxisFormat,
|
||||
getAxisFormat,
|
||||
setTodayMarker,
|
||||
getTodayMarker,
|
||||
setTitle,
|
||||
getTitle,
|
||||
addSection,
|
||||
|
@@ -21,6 +21,7 @@ describe('when using the ganttDb', function() {
|
||||
beforeEach(function() {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||
ganttDb.enableInclusiveEndDates();
|
||||
ganttDb.setTodayMarker('off');
|
||||
ganttDb.setExcludes('weekends 2019-02-06,friday');
|
||||
ganttDb.addSection('weekends skip test');
|
||||
ganttDb.addTask('test1', 'id1,2019-02-01,1d');
|
||||
@@ -34,6 +35,7 @@ describe('when using the ganttDb', function() {
|
||||
${'getTitle'} | ${''}
|
||||
${'getDateFormat'} | ${''}
|
||||
${'getAxisFormat'} | ${''}
|
||||
${'getTodayMarker'} | ${''}
|
||||
${'getExcludes'} | ${[]}
|
||||
${'getSections'} | ${[]}
|
||||
${'endDatesAreInclusive'} | ${false}
|
||||
@@ -216,4 +218,13 @@ describe('when using the ganttDb', function() {
|
||||
expect(tasks[1].task).toEqual('test2');
|
||||
});
|
||||
});
|
||||
|
||||
it.each`
|
||||
type | expected
|
||||
${'hide'} | ${'off'}
|
||||
${'style'} | ${'stoke:stroke-width:5px,stroke:#00f,opacity:0.5'}
|
||||
`('should ${type} today marker', ({ expected }) => {
|
||||
ganttDb.setTodayMarker(expected);
|
||||
expect(ganttDb.getTodayMarker()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
@@ -396,17 +396,25 @@ export const draw = function(text, id) {
|
||||
}
|
||||
|
||||
function drawToday(theSidePad, theTopPad, w, h) {
|
||||
const todayMarker = ganttDb.getTodayMarker();
|
||||
if (todayMarker === 'off') {
|
||||
return;
|
||||
}
|
||||
|
||||
const todayG = svg.append('g').attr('class', 'today');
|
||||
|
||||
const today = new Date();
|
||||
const todayLine = todayG.append('line');
|
||||
|
||||
todayG
|
||||
.append('line')
|
||||
todayLine
|
||||
.attr('x1', timeScale(today) + theSidePad)
|
||||
.attr('x2', timeScale(today) + theSidePad)
|
||||
.attr('y1', conf.titleTopMargin)
|
||||
.attr('y2', h - conf.titleTopMargin)
|
||||
.attr('class', 'today');
|
||||
|
||||
if (todayMarker !== '') {
|
||||
todayLine.attr('style', todayMarker.replace(/,/g, ';'));
|
||||
}
|
||||
}
|
||||
|
||||
// from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
|
||||
|
@@ -55,13 +55,14 @@ that id.
|
||||
|
||||
"gantt" return 'gantt';
|
||||
"dateFormat"\s[^#\n;]+ return 'dateFormat';
|
||||
"inclusiveEndDates" return 'inclusiveEndDates';
|
||||
"inclusiveEndDates" return 'inclusiveEndDates';
|
||||
"axisFormat"\s[^#\n;]+ return 'axisFormat';
|
||||
"excludes"\s[^#\n;]+ return 'excludes';
|
||||
"todayMarker"\s[^\n;]+ return 'todayMarker';
|
||||
\d\d\d\d"-"\d\d"-"\d\d return 'date';
|
||||
"title"\s[^#\n;]+ return 'title';
|
||||
"section"\s[^#:\n;]+ return 'section';
|
||||
[^#:\n;]+ return 'taskTxt';
|
||||
[^#:\n;]+ return 'taskTxt';
|
||||
":"[^#\n;]+ return 'taskData';
|
||||
":" return ':';
|
||||
<<EOF>> return 'EOF';
|
||||
@@ -93,9 +94,10 @@ line
|
||||
|
||||
statement
|
||||
: dateFormat {yy.setDateFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| inclusiveEndDates {yy.enableInclusiveEndDates();$$=$1.substr(18);}
|
||||
| inclusiveEndDates {yy.enableInclusiveEndDates();$$=$1.substr(18);}
|
||||
| axisFormat {yy.setAxisFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| excludes {yy.setExcludes($1.substr(9));$$=$1.substr(9);}
|
||||
| todayMarker {yy.setTodayMarker($1.substr(12));$$=$1.substr(12);}
|
||||
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
||||
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| clickStatement
|
||||
|
@@ -37,6 +37,14 @@ describe('when parsing a gantt diagram it', function() {
|
||||
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
});
|
||||
it('should handle a todayMarker definition', function() {
|
||||
spyOn(ganttDb, 'setTodayMarker');
|
||||
const str =
|
||||
'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid\nexcludes weekdays 2019-02-01\ntodayMarker off';
|
||||
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(ganttDb.setTodayMarker).toHaveBeenCalledWith('off');
|
||||
});
|
||||
it('should handle a section definition', function() {
|
||||
const str =
|
||||
'gantt\n' +
|
||||
|
@@ -362,7 +362,7 @@ const drawMessage = function(elem, startx, stopx, verticalPos, msg, sequenceInde
|
||||
line.attr('marker-start', 'url(' + url + '#sequencenumber)');
|
||||
g.append('text')
|
||||
.attr('x', startx)
|
||||
.attr('y', verticalPos + 4)
|
||||
.attr('y', verticalPos + 4 + totalOffset)
|
||||
.attr('font-family', 'sans-serif')
|
||||
.attr('font-size', '12px')
|
||||
.attr('text-anchor', 'middle')
|
||||
|
@@ -75,8 +75,13 @@ export const draw = function(text, id) {
|
||||
const width = bounds.width + padding * 2;
|
||||
const height = bounds.height + padding * 2;
|
||||
|
||||
// Zoom in a bit
|
||||
diagram.attr('width', width * 1.75);
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', `max-width: ${width * 1.75}px;`);
|
||||
} else {
|
||||
// Zoom in a bit
|
||||
diagram.attr('width', width * 1.75);
|
||||
}
|
||||
// diagram.attr('height', bounds.height * 3 + conf.padding * 2);
|
||||
diagram.attr(
|
||||
'viewBox',
|
||||
|
Reference in New Issue
Block a user