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/