lit ace markdown编辑器
src/components/ace-markdown.ts
:
import { LitElement, css, html } from "lit";
import { customElement, property, query } from "lit/decorators.js";
// import ace from "ace-builds/src-min-noconflict/ace.js";
import ace from "ace-builds/src/ace.js";
import { marked } from "marked";
@customElement("ace-markdown")
export class AceMarkdown extends LitElement {
@query("#container")
containerElement!: HTMLDivElement;
@property({ type: String })
content = "# Hello World";
private editor!: any;
@query("#editor")
editorElement!: HTMLDivElement;
@property({ type: Boolean })
fullscreen = false;
@query("#previewer")
previewerElement!: HTMLDivElement;
firstUpdated() {
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();
}
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() {
const fullscreenIcon = this.fullscreen ? "-" : "+";
return html`
<div id="container" style="height: 40vh">
<div>
<div style="display: flex; justify-content: end">
<button @click=${this.save}>下载</button>
<button title="全屏/还原" @click=${this.fullscreenSwitch}>${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();
}
}
declare global {
interface HTMLElementTagNameMap {
"ace-markdown": AceMarkdown;
}
}
justfile
:
build:
#!/usr/bin/env bash
bun build src/components/ace.markdown.ts --outfile public/ace.markdown.js --minify
cp node_modules/ace-builds/src-min-noconflict/mode-markdown.js public/
cp node_modules/ace-builds/src-min-noconflict/theme-monokai.js public/
src/content/docs/demo.mdx
:
---
title: Demonstrate lit components
description: Demostrate lit components.
template: splash
---
<ace-markdown />
<ace-markdown />
<script type="module" src="ace-markdown.js"></script>
目前存在的问题:当输入内容改变时,存在parentNode
为null,导致parentNode.insertBefore
调用失败,而非shadowDom不存在这种问题。在测试过程中,还发现跟节点操作相关的其它错误。这些错误都发生在lit-html
中。