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中。

posted @ 2024-08-20 10:53  卓能文  阅读(8)  评论(0编辑  收藏  举报