Skip to content

Commit 1620e59

Browse files
feat: enable mermaid with zoom, pan and download (#1470)
1 parent 555d43b commit 1620e59

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed

assets/js/mermaid.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,120 @@
3333
var settings = norm(mermaid.mermaidAPI.defaultConfig, params);
3434
settings.startOnLoad = true;
3535
mermaid.initialize(settings);
36+
37+
function ensureModal() {
38+
if (!$('#mermaidModal').length) {
39+
var html = '' +
40+
'<div class="modal fade" id="mermaidModal" tabindex="-1" role="dialog" aria-hidden="true">' +
41+
'<div class="modal-dialog modal-dialog-centered modal-xl" role="document">' +
42+
'<div class="modal-content">' +
43+
'<div class="modal-body">' +
44+
'<div class="mermaid-zoom-controls">' +
45+
'<button type="button" class="btn btn-sm btn-light" id="mermaidZoomIn">+</button>' +
46+
'<button type="button" class="btn btn-sm btn-light" id="mermaidZoomOut">-</button>' +
47+
'<button type="button" class="btn btn-sm btn-light" id="mermaidZoomReset">Reset</button>' +
48+
'<button type="button" class="btn btn-sm btn-light" id="mermaidDownload">Download</button>' +
49+
'</div>' +
50+
'<div class="mermaid-zoom-wrapper"><div class="mermaid-zoom-content"></div></div>' +
51+
'</div>' +
52+
'</div>' +
53+
'</div>' +
54+
'</div>';
55+
$('body').append(html);
56+
}
57+
}
58+
59+
var scale = 1;
60+
function applyScale() {
61+
$('.mermaid-zoom-content').css('transform', 'scale(' + scale + ')');
62+
}
63+
64+
$(document).on('click', 'pre.mermaid, .mermaid', function() {
65+
ensureModal();
66+
var $svg = $(this).find('svg').clone();
67+
var $content = $('#mermaidModal .mermaid-zoom-content');
68+
$content.empty().append($svg);
69+
scale = 1;
70+
applyScale();
71+
$('#mermaidModal').modal('show');
72+
});
73+
74+
$(document).on('click', '#mermaidZoomIn', function() {
75+
scale = Math.min(scale + 0.2, 5);
76+
applyScale();
77+
});
78+
$(document).on('click', '#mermaidZoomOut', function() {
79+
scale = Math.max(scale - 0.2, 0.2);
80+
applyScale();
81+
});
82+
$(document).on('click', '#mermaidZoomReset', function() {
83+
scale = 1;
84+
applyScale();
85+
});
86+
$(document).on('wheel', '.mermaid-zoom-wrapper', function(e) {
87+
e.preventDefault();
88+
var dy = e.originalEvent.deltaY;
89+
scale += (dy > 0 ? -0.1 : 0.1);
90+
scale = Math.max(0.2, Math.min(5, scale));
91+
applyScale();
92+
});
93+
94+
var isDragging = false;
95+
var startX = 0;
96+
var startY = 0;
97+
var scrollLeft = 0;
98+
var scrollTop = 0;
99+
100+
$(document).on('mousedown touchstart', '.mermaid-zoom-wrapper', function(e) {
101+
var $wrap = $(this);
102+
isDragging = true;
103+
$wrap.addClass('dragging');
104+
var ev = e.type === 'touchstart' ? e.originalEvent.touches[0] : e;
105+
startX = ev.pageX - $wrap.offset().left;
106+
startY = ev.pageY - $wrap.offset().top;
107+
scrollLeft = $wrap.scrollLeft();
108+
scrollTop = $wrap.scrollTop();
109+
});
110+
111+
$(document).on('mousemove touchmove', '.mermaid-zoom-wrapper', function(e) {
112+
if (!isDragging) return;
113+
var $wrap = $(this);
114+
var ev = e.type === 'touchmove' ? e.originalEvent.touches[0] : e;
115+
var x = ev.pageX - $wrap.offset().left;
116+
var y = ev.pageY - $wrap.offset().top;
117+
var dx = x - startX;
118+
var dy = y - startY;
119+
$wrap.scrollLeft(scrollLeft - dx);
120+
$wrap.scrollTop(scrollTop - dy);
121+
});
122+
123+
$(document).on('mouseup mouseleave touchend', '.mermaid-zoom-wrapper', function() {
124+
isDragging = false;
125+
$(this).removeClass('dragging');
126+
});
127+
128+
$(document).on('click', '#mermaidDownload', function() {
129+
var $svg = $('#mermaidModal .mermaid-zoom-content svg');
130+
if (!$svg.length) return;
131+
var node = $svg.get(0);
132+
if (!node.getAttribute('xmlns')) {
133+
node.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
134+
}
135+
var serializer = new XMLSerializer();
136+
var source = serializer.serializeToString(node);
137+
var blob = new Blob([source], { type: 'image/svg+xml;charset=utf-8' });
138+
var url = URL.createObjectURL(blob);
139+
var a = document.createElement('a');
140+
var name = (document.title || 'diagram') + '-' + Date.now() + '.svg';
141+
a.href = url;
142+
a.download = name;
143+
document.body.appendChild(a);
144+
a.click();
145+
setTimeout(function() {
146+
URL.revokeObjectURL(url);
147+
a.remove();
148+
}, 0);
149+
});
36150
})(jQuery);
37151
{{ end }}
38152
{{ end }}

assets/scss/_styles_project.scss

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,37 @@ assets/scss/_styles_project.scss
2323
margin-bottom: initial;
2424
}
2525
}
26+
27+
pre.mermaid {
28+
padding: 0;
29+
margin: 0;
30+
background: transparent;
31+
border: 0;
32+
}
33+
34+
.mermaid svg {
35+
width: 100%;
36+
height: auto;
37+
}
38+
39+
.mermaid-zoom-wrapper {
40+
overflow: auto;
41+
max-height: 85vh;
42+
cursor: grab;
43+
user-select: none;
44+
}
45+
46+
.mermaid-zoom-content {
47+
transform-origin: 0 0;
48+
}
49+
50+
.mermaid-zoom-wrapper.dragging {
51+
cursor: grabbing;
52+
}
53+
54+
.mermaid-zoom-controls {
55+
position: absolute;
56+
right: 1rem;
57+
top: 1rem;
58+
z-index: 10;
59+
}

assets/scss/markdown.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@
3939
overflow: auto
4040
}
4141

42+
.markdown pre.mermaid {
43+
padding: 0;
44+
margin: 0;
45+
overflow: visible
46+
}
47+
4248
.markdown div[class*=language-] pre code {
4349
color: rgba(0,0,0,.5)
4450
}

config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ offlineSearch = false
161161
# Enable syntax highlighting and copy buttons on code blocks with Prism
162162
prism_syntax_highlighting = true
163163

164+
# Mermaid diagrams configuration
165+
[params.mermaid]
166+
enable = true
167+
theme = "default"
168+
securitylevel = "strict"
169+
164170
# User interface configuration
165171
[params.ui]
166172
# Enable to show the side bar menu in its compact state.

0 commit comments

Comments
 (0)