1
0
Fork 0
mirror of https://github.com/kou029w/quot.git synced 2025-01-19 16:38:06 +00:00
quot/app/views/components/editor.tsx

99 lines
3 KiB
TypeScript
Raw Normal View History

2022-08-28 17:49:49 +09:00
import {
$createParagraphNode,
$createTextNode,
$getRoot,
2022-09-07 17:03:54 +09:00
$isElementNode,
2022-08-28 17:49:49 +09:00
createEditor,
} from "lexical";
2022-09-07 00:47:55 +09:00
import { registerHistory, createEmptyHistoryState } from "@lexical/history";
2022-09-07 17:03:54 +09:00
import {
$createHeadingNode,
HeadingNode,
registerRichText,
} from "@lexical/rich-text";
2022-09-07 19:09:24 +09:00
import { $createAutoLinkNode, AutoLinkNode } from "@lexical/link";
2022-08-28 17:49:49 +09:00
import { onCleanup, onMount } from "solid-js";
2022-09-01 23:54:20 +09:00
import type Pages from "../../protocol/pages";
2022-08-23 09:49:34 +09:00
import "./editor.css";
2022-09-07 19:09:24 +09:00
const urlMatcher = /https?:\/\/[^\s]+/;
const editor = createEditor({ nodes: [HeadingNode, AutoLinkNode] });
2022-08-28 17:49:49 +09:00
function ref(el: HTMLElement) {
editor.setRootElement(el);
}
2022-08-24 12:49:02 +09:00
export default (props: {
2022-08-24 09:50:35 +09:00
id: number;
2022-08-28 17:49:49 +09:00
title: string;
text: string;
2022-08-24 12:49:02 +09:00
onUpdatePage: (content: Pages.RequestContentPage) => void;
2022-08-28 17:49:49 +09:00
}) => {
const initialEditorState = () => {
const root = $getRoot();
2022-09-07 19:09:24 +09:00
const [title, ...lines] = props.text.split("\n");
const titleNode = $createHeadingNode("h2");
titleNode.append($createTextNode(title));
root.append(titleNode);
for (const line of lines) {
const lineNode = $createParagraphNode();
const indent = line.match(/^\s*/)?.[0]?.length ?? 0;
lineNode.setIndent(indent);
let text = line.slice(indent);
let match: RegExpMatchArray | null = null;
while ((match = text.match(urlMatcher))) {
const offset = text.slice(0, match.index!);
const input = match[0]!;
const link = $createAutoLinkNode(input);
link.append($createTextNode(match[0]));
lineNode.append($createTextNode(offset), link);
text = text.slice(offset.length + input.length);
}
lineNode.append($createTextNode(text));
root.append(lineNode);
2022-09-07 17:03:54 +09:00
}
2022-08-28 17:49:49 +09:00
};
2022-09-07 17:03:54 +09:00
onCleanup(registerRichText(editor, initialEditorState));
2022-09-07 00:47:55 +09:00
onCleanup(registerHistory(editor, createEmptyHistoryState(), 333));
2022-09-07 17:03:54 +09:00
onCleanup(
editor.registerUpdateListener(() =>
editor.update(() => {
const root = $getRoot();
2022-09-07 17:48:18 +09:00
const defaultTitle = new Date()
.toLocaleDateString(navigator.language, {
year: "numeric",
month: "2-digit",
day: "2-digit",
})
.replaceAll("/", "-");
const [titleNode, ...lineNodes] = root.getChildren();
const title =
titleNode?.getTextContent().trim() ||
(lineNodes.length === 0 ? "" : defaultTitle);
const lines = lineNodes.map((line) => {
2022-09-07 17:25:15 +09:00
const indent = $isElementNode(line) ? line.getIndent() : 0;
return `${" ".repeat(indent)}${line.getTextContent()}`;
});
2022-09-07 17:48:18 +09:00
const text = [title, ...lines].join("\n");
2022-09-07 19:54:48 +09:00
if (props.text !== text) {
props.onUpdatePage({ id: props.id, title, text });
}
2022-08-28 17:49:49 +09:00
})
2022-09-07 17:03:54 +09:00
)
);
onMount(() => editor.focus());
2022-09-07 19:09:24 +09:00
return (
<article
ref={ref}
onclick={(e) => {
const el = e.target.parentElement;
if (el instanceof HTMLAnchorElement) {
window.open(el.href, "_blank", "noreferrer");
}
}}
class="editor"
contenteditable
/>
);
2022-08-28 17:49:49 +09:00
};