Skip to content

Instantly share code, notes, and snippets.

@CarsonSlovoka
Last active August 25, 2023 02:32
Show Gist options
  • Save CarsonSlovoka/34478f75a9f0a27b06d7cf92d6427ade to your computer and use it in GitHub Desktop.
Save CarsonSlovoka/34478f75a9f0a27b06d7cf92d6427ade to your computer and use it in GitHub Desktop.
slidev component: markMap
<!--
Usage:
<markMap>
<pre>
Root
group1
sub1
<a href=''>sub2</a>
msg
hello <span class="green">world</span>
G2
</pre>
</markMap>
-->
<script setup lang="ts">
import {onMounted} from "vue";
const props = defineProps({
indentSize: {
type: Number,
default: 4
},
style: {
type: String,
default: ""
},
class: {
type: String,
default: ""
},
colorFreezeLevel: {
type: Number,
default: 5
}
})
function LoadKatexFont() {
const links = document.querySelectorAll('link[rel="stylesheet"]')
let runWebFontConfig = false
for (const link of links) {
const url = link.getAttribute('href')
const parts = url.split('/')
const filename = parts[parts.length - 1]
if (filename === "katex.min.css") {
runWebFontConfig = true
}
}
if (runWebFontConfig) {
window.WebFontConfig = {
custom: {
families: [
"KaTeX_AMS",
"KaTeX_Caligraphic:n4,n7",
"KaTeX_Fraktur:n4,n7",
"KaTeX_Main:n4,n7,i4,i7",
"KaTeX_Math:i4,i7",
"KaTeX_Script",
"KaTeX_SansSerif:n4,n7,i4",
"KaTeX_Size1", "KaTeX_Size2",
"KaTeX_Size3",
"KaTeX_Size4",
"KaTeX_Typewriter"
]
},
active: () => {
window.markmap.refreshHook.call()
}
}
}
}
function* readLines(content) {
const lines = content.split("\n").filter(line => {
const isComment = line.match(/^\s*\/\//) // 如果是 // 開頭的也忽略掉,視為註解
return line !== "" && !isComment
})
for (const line of lines) {
yield line
}
}
function Node(depth, content, parent = null, children = []) {
this.depth = depth
this.content = content
this.children = children
this.parent = parent
}
function dataToMarkMap(reader, parent = {}, indentSize = 4) {
const {value, done} = reader.next()
if (done) {
return
}
const match = value.match(/^(\s*)(?<content>.*)/)
const leadingWhiteSpace = match[1].length
const content = match.groups.content
const depth = leadingWhiteSpace / indentSize
if (depth === 0) {
parent.depth = 0
parent.content = content
parent.children = []
parent.parent = null
return dataToMarkMap(reader, parent, indentSize)
} else {
const node = new Node(depth, content)
if (node.depth > parent.depth) {
node.parent = parent
parent.children.push(node)
} else {
// 往上尋找父節點
let tmpParent = parent
while (1) {
tmpParent = tmpParent.parent
if (node.depth > tmpParent.depth) {
node.parent = tmpParent
tmpParent.children.push(node)
break
}
if (tmpParent.parent === null) {
console.error("no parent node")
return
}
}
}
return dataToMarkMap(reader, node, indentSize)
}
}
async function MarkMapCheck() {
while (1) {
const ok = await new Promise((resolve) => {
setTimeout(() => {
if (window.markmap === undefined && d3 === undefined) {
resolve(false)
} else {
resolve(true)
}
}, 250);
})
if (ok) {
break
}
}
}
onMounted(async () => {
LoadKatexFont();
await MarkMapCheck();
[...document.querySelectorAll("div.markMapBody")].forEach((e, i) => {
const preElem = e.querySelector("pre")
if (preElem === null) {
return
}
const content = preElem.innerHTML // preElem.innerText
const lineGenerator = readLines(content)
const frag = document.createRange().createContextualFragment(`<svg class="markMap" id="markMap-${i}">
<rect width="100%" height="100%" class="markmap-bg"/>
</svg>`)
e.innerHTML = ""
e.append(frag)
const obj = {}
dataToMarkMap(lineGenerator, obj, props.indentSize);
// JSON.stringify(result, null, 2)
((M, opt) => {
const I = window.markmap
window.mm = I.Markmap.create(
`svg#markMap-${i}`,
(I.deriveOptions)(opt),
M
)
})(obj, {"colorFreezeLevel": props.colorFreezeLevel});
})
})
</script>
<template>
<component src="https://cdn.jsdelivr.net/npm/d3@7.8.5" :is="'script'"></component>
<component src="https://cdn.jsdelivr.net/npm/markmap-view@0.15.3" :is="'script'"></component>
<component src="https://cdn.jsdelivr.net/npm/webfontloader@1.6.28/webfontloader.js" :is="'script'"></component>
<div :class="['markMapBody', props.class]" :style="props.style">
<slot/>
</div>
</template>
<style>
.markMap {
display: block;
width: 100vw;
height: 100vh;
}
rect.markmap-bg {
fill: var(--prism-background, white)
}
/* for foreignObject div */
.markmap-foreign {
color: var(--prism-foreground, black)
}
</style>
@CarsonSlovoka
Copy link
Author

image

@CarsonSlovoka
Copy link
Author

darkMode support:

out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment