Last active
August 25, 2023 02:32
-
-
Save CarsonSlovoka/34478f75a9f0a27b06d7cf92d6427ade to your computer and use it in GitHub Desktop.
slidev component: markMap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
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> |
Author
CarsonSlovoka
commented
Aug 24, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment