src/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ace-markdown></ace-markdown>
<ace-markdown></ace-markdown>
<script type="module" src="./ace.markdown.ts"></script>
</body>
</html>
src/ace.markdown.ts
:
import ace from "ace-builds/src-min-noconflict/ace.js";
import { marked } from "marked";
class AceMarkdown extends HTMLElement {
attatched = false;
containerElement!: HTMLDivElement;
content = "# Hello world";
private editor!: any;
editorElement!: HTMLDivElement;
fullElement!: HTMLButtonElement;
fullscreen = false;
previewerElement!: HTMLDivElement;
saveElement!: HTMLButtonElement;
connectedCallback() {
if (!this.attatched) {
this.attatched = true;
this.containerElement = this.shadowRoot?.querySelector("#container") as HTMLDivElement;
this.editorElement = this.shadowRoot?.querySelector("#editor") as HTMLDivElement;
this.fullElement = this.shadowRoot?.querySelector("#full") as HTMLButtonElement;
this.previewerElement = this.shadowRoot?.querySelector("#previewer") as HTMLDivElement;
this.saveElement = this.shadowRoot?.querySelector("#save") as HTMLButtonElement;
ace.config.set("basePath", "/");
const editor = ace.edit(this.editorElement, {
// fontSize: "14px",
mode: "ace/mode/markdown",
// theme: "ace/theme/monokai",
value: this.content,
wrap: true,
});
this.editor = editor;
editor.renderer.attachToShadowRoot(); // !!!important
editor.on("input", () => {
this.updatePreview();
});
this.updatePreview();
this.fullElement.onclick = () => {
this.fullscreenSwitch();
}
this.saveElement.onclick = () => {
this.save();
}
}
}
constructor() {
super();
this.attachShadow({ mode: "open" });
this.render();
}
fullscreenIcon() {
return this.fullscreen ? "-" : "+";
}
fullscreenSwitch() {
this.fullscreen = !this.fullscreen;
if (this.fullscreen) {
this.containerElement.requestFullscreen();
this.containerElement.style.height = "100vh";
} else {
document.exitFullscreen();
this.containerElement.style.height = "40vh";
}
}
render() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = `<div id="container" style="height: 40vh">
<div>
<div style="display: flex; justify-content: end">
<button id="save">下载</button>
<button id="full" title="全屏/还原">${this.fullscreenIcon()}</button>
</div>
</div>
<div style="display: flex; height: 100%">
<div style="flex: 50%">
<div id="editor" style="width: 100%; height: 100%;">${this.content}</div>
</div>
<div style="flex: 50%">
<div id="previewer" style="width: 100%; height: 100%; overflow: auto"></div>
</div>
</div>
</div>`;
}
}
save() {
const fileParts = [this.content];
const blob = new Blob(fileParts, { type: "text/plain" });
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "paper.md";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
updatePreview() {
this.content = this.editor.getValue();
this.previewerElement.innerHTML = marked(this.content).toString();
}
}
customElements.define("ace-markdown", AceMarkdown);
declare global {
interface HTMLElementTagNameMap {
"ace-markdown": AceMarkdown;
}
}
justfile
:
build:
#!/usr/bin/env bash
rm -rf dist
parcel build ./src/**.html --public-url . --no-source-maps
cp node_modules/ace-builds/src-min-noconflict/mode-markdown.js dist/
cp node_modules/ace-builds/src-min-noconflict/theme-monokai.js dist/
标签:web,markdown,ace,src,content,editor,shadowRoot
From: https://www.cnblogs.com/soarowl/p/18371019