feat(editor) #11: integrate Maturity Matrix editor in markdown view

Add a sidebar button and modal that embeds the Couchbase Use Case Maturity
Assessment Matrix as an iframe. Saves the matrix as a readable markdown
block wrapped in <!--maturity-matrix--> comments and round-trips through
parseMarkdown on edit. Mirrors the draw.io integration pattern.
pull/8001/head
Tayeb Chlyah 2 months ago
parent 3902bb9bf4
commit 2545993ed7

@ -86,7 +86,8 @@ export default {
editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-media.vue'),
editorModalBlocks: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-blocks.vue'),
editorModalConflict: () => import(/* webpackChunkName: "editor-conflict", webpackMode: "lazy" */ './editor/editor-modal-conflict.vue'),
editorModalDrawio: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-drawio.vue')
editorModalDrawio: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-drawio.vue'),
editorModalMaturityMatrix: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-maturitymatrix.vue')
},
props: {
locale: {

@ -127,6 +127,11 @@
v-btn.mt-3.animated.fadeInLeft.wait-p2s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalDrawio`)').mx-0
v-icon mdi-chart-multiline
span {{$t('editor:markup.insertDiagram')}}
v-tooltip(right, color='teal')
template(v-slot:activator='{ on }')
v-btn.mt-3.animated.fadeInLeft.wait-p3s(icon, tile, v-on='on', dark, @click='openMaturityMatrix').mx-0
v-icon mdi-table-check
span Insert Maturity Matrix
template(v-if='$vuetify.breakpoint.mdAndUp')
v-spacer
v-tooltip(right, color='teal')
@ -459,7 +464,7 @@ export default {
processContent (newContent) {
linesMap = []
// this.$store.set('editor/content', newContent)
this.processMarkers(this.cm.firstLine(), this.cm.lastLine())
this.processMarkers(this.cm.firstLine(), this.cm.lastLine() + 1)
this.previewHTML = DOMPurify.sanitize(md.render(newContent), {
ADD_TAGS: ['foreignObject'],
HTML_INTEGRATION_POINTS: { foreignobject: true }
@ -663,6 +668,10 @@ export default {
insertLink () {
this.insertLinkDialog = true
},
openMaturityMatrix () {
this.$store.set('editor/activeModalData', null)
this.toggleModal(`editorModalMaturityMatrix`)
},
insertLinkHandler ({ locale, path }) {
const lastPart = _.last(path.split('/'))
this.insertAtCursor({
@ -682,7 +691,33 @@ export default {
if (ln.text.startsWith('```diagram')) {
found = 'diagram'
foundStart = line
} else if (ln.text === '```' && found) {
} else if (ln.text === '<!--maturity-matrix-->') {
found = 'maturity-matrix'
foundStart = line
} else if (ln.text === '<!--/maturity-matrix-->' && found === 'maturity-matrix') {
const startLine = foundStart
const endLine = line
const startLineLen = this.cm.doc.getLine(startLine).length
this.addMarker({
kind: 'maturity-matrix',
from: { line: startLine, ch: 0 },
to: { line: startLine, ch: startLineLen },
text: 'Edit Maturity Matrix',
action: ((start, end) => {
return (ev) => {
const endLen = this.cm.doc.getLine(end).length
this.cm.doc.setSelection({ line: start, ch: 0 }, { line: end, ch: endLen })
const innerLines = []
for (let i = start + 1; i < end; i++) {
innerLines.push(this.cm.doc.getLine(i))
}
this.$store.set('editor/activeModalData', innerLines.join('\n'))
this.toggleModal(`editorModalMaturityMatrix`)
}
})(startLine, endLine)
})
found = null
} else if (ln.text === '```' && found === 'diagram') {
switch (found) {
// ------------------------------
// -> DIAGRAM
@ -851,6 +886,15 @@ export default {
this.cm.doc.replaceSelection('```diagram\n' + opts.text + '\n```\n', 'start')
this.processMarkers(selStartLine, selEndLine)
break
case 'MATURITY_MATRIX': {
const mmStart = this.cm.getCursor('from').line
const wrapped = '<!--maturity-matrix-->\n' + opts.markdown + '\n<!--/maturity-matrix-->\n'
const wrappedLineCount = wrapped.split('\n').length
this.cm.doc.replaceSelection(wrapped, 'start')
// replaceSelection(_, 'start') leaves cursor at start; compute end from inserted line count
this.processMarkers(mmStart, mmStart + wrappedLineCount + 1)
break
}
}
})

@ -0,0 +1,83 @@
<template lang='pug'>
v-card.editor-modal-maturitymatrix.animated.fadeIn(flat, tile)
iframe(
ref='matrix'
src='/_assets/maturity-matrix/index.html'
frameborder='0'
)
</template>
<script>
import { sync } from 'vuex-pathify'
export default {
computed: {
activeModal: sync('editor/activeModal')
},
methods: {
close () {
this.activeModal = ''
},
send (msg) {
if (this.$refs.matrix && this.$refs.matrix.contentWindow) {
this.$refs.matrix.contentWindow.postMessage(msg, '*')
}
},
receive (evt) {
if (!this.$refs.matrix || evt.source !== this.$refs.matrix.contentWindow) {
return
}
const msg = evt.data
if (!msg || typeof msg !== 'object') {
return
}
switch (msg.event) {
case 'ready': {
this.send({ action: 'init', markdown: this.$store.get('editor/activeModalData') })
this.$store.set('editor/activeModalData', null)
break
}
case 'save': {
this.$root.$emit('editorInsert', {
kind: 'MATURITY_MATRIX',
markdown: msg.markdown
})
this.close()
break
}
case 'exit': {
this.close()
break
}
}
}
},
mounted () {
window.addEventListener('message', this.receive)
},
beforeDestroy () {
window.removeEventListener('message', this.receive)
}
}
</script>
<style lang='scss'>
.editor-modal-maturitymatrix {
position: fixed !important;
top: 0;
left: 0;
z-index: 10;
width: 100%;
height: 100vh;
background-color: rgba(255,255,255, 1) !important;
overflow: hidden;
> iframe {
width: 100%;
height: 100vh;
border: 0;
padding: 0;
background-color: #FFF;
}
}
</style>

File diff suppressed because it is too large Load Diff

@ -14,7 +14,13 @@ module.exports = function (req, res, next) {
// -> Disable Frame Embedding
if (WIKI.config.security.securityIframe) {
res.set('X-Frame-Options', 'deny')
// Allow same-origin embedding for the Maturity Matrix editor asset
// (loaded inside the editor as an iframe). Everything else stays denied.
if (req.path.startsWith('/_assets/maturity-matrix/')) {
res.set('X-Frame-Options', 'sameorigin')
} else {
res.set('X-Frame-Options', 'deny')
}
}
// -> Re-enable XSS Fitler if disabled

Loading…
Cancel
Save