omi ace markdown editor

卓能文發表於2024-09-03

omi是騰訊開源的一款web components開發框架。

AceMarkdown.tsx:

import { Component, createRef, h, signal, tag } from "omi";
import * as ace from "ace-builds";
import { marked } from "marked";

@tag("ace-markdown")
class AceMarkdown extends Component {
	containerElement = createRef<HTMLDivElement>();
	editor: ace.Ace.Editor | null = null;
	editorElement = createRef<HTMLDivElement>();
	fullscreen = signal(false);
	previewerElement = createRef<HTMLDivElement>();

	fullscreenIcon() {
		return this.fullscreen.value ? "-" : "+";
	}

	fullscreenSwitch() {
		this.fullscreen.value = !this.fullscreen.value;
		if (this.containerElement.current) {
			if (this.fullscreen.value) {
				this.containerElement.current.requestFullscreen();
				this.containerElement.current.style.height = "100vh";
			} else {
				document.exitFullscreen();
				this.containerElement.current.style.height = "40vh";
			}
		}
	}

	installed() {
		if (this.editorElement.current) {
			ace.config.set("basePath", "/ace");
			const editor = ace.edit(this.editorElement.current, {
				// fontSize: "14px",
				mode: "ace/mode/markdown",
				// theme: "ace/theme/monokai",
				wrap: true,
			});
			this.editor = editor;
			editor.renderer.attachToShadowRoot(); // !!!important
			editor.on("input", () => {
				this.updatePreview();
			});
		}
	}

	render() {
		return (
			<div ref={this.containerElement} style="height: 40vh">
				<div style="display: flex; justify-content: end">
					<button type="button">下載</button>
					<button
						onClick={() => {
							this.fullscreenSwitch();
						}}
						title="全屏/還原"
						type="button"
					>
						{this.fullscreenIcon()}
					</button>
				</div>
				<div style="display: flex; height: 100%">
					<div style="flex: 50%">
						<div ref={this.editorElement} style="width: 100%; height: 100%;">
							{" "}
						</div>
					</div>
					<div style="flex: 50%">
						<div
							ref={this.previewerElement}
							style="width: 100%; height: 100%; overflow: auto"
						>
							{" "}
						</div>
					</div>
				</div>
			</div>
		);
	}

	save() {
		if (this.editor) {
			const fileParts = [this.editor.getValue()];
			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() {
		if (this.previewerElement.current && this.editor) {
			this.previewerElement.current.innerHTML = marked(
				this.editor.getValue(),
			).toString();
		}
	}
}

export default AceMarkdown;

justfile:

build:
    #!/usr/bin/env bash
    cp node_modules/ace-builds/src-min-noconflict/mode-markdown.js public/ace/
    cp node_modules/ace-builds/src-min-noconflict/theme-monokai.js public/ace/

相關文章