v3/src/editor/Editor.tsx

90 lines
3.5 KiB
TypeScript

"use client";
import * as Y from "yjs";
import { type InitialConfigType, LexicalComposer } from "@lexical/react/LexicalComposer";
import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
import { CollaborationPlugin } from '@lexical/react/LexicalCollaborationPlugin';
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import { HeaderMarkerNode, HeaderNode } from "./nodes/header_node";
import { ParagraphPlugin } from "./plugins/paragraph_plugin";
import { HeaderPlugin } from "./plugins/header_plugin";
import { SelectionPlugin } from "./plugins/selection_plugin";
import { FormattedTextMarkerNode, FormattedTextNode } from "./nodes/formatted_text";
import { FormattedTextPlugin } from "./plugins/formatted_text_plugin";
import { TaskIconNode, TaskMarkerNode, TaskNode } from "./nodes/task_node";
import { TaskPlugin } from "./plugins/task_plugin";
import { LinkIconNode, LinkMarkerNode, LinkNode, LinkUrlNode } from "./nodes/link_node";
import { LinkPlugin } from "./plugins/link_plugin";
import { TermIconNode, TermMarkerNode, TermNode } from "./nodes/term_node";
import { TermPlugin } from "./plugins/term_plugin";
import { useCallback } from "react";
import type { Provider } from "@lexical/yjs";
import type { NoteId } from "~/lib/metadata";
import type { Klass, LexicalNode } from "lexical";
import { useNoteDoc, useNoteProviders } from "~/hooks/use-note";
export function Editor(props: { noteId: NoteId }) {
const provider = useNoteProviders().websocket;
const doc = useNoteDoc();
const providerFactory = useCallback((id: string, yjsDocMap: Map<string, Y.Doc>): Provider => {
// Just overwrite it, because we have the doc managed externally
yjsDocMap.set(id, doc);
return provider as any as Provider;
}, [provider, doc]);
return (
<LexicalComposer initialConfig={initialEditorConfig}>
<div className="w-full relative editor-content">
<PlainTextPlugin
contentEditable={<ContentEditable className="outline-none" />}
placeholder={<div className="absolute top-0 left-0 pointer-events-none text-zinc-300">Enter some text...</div>}
ErrorBoundary={LexicalErrorBoundary}
/>
</div>
<CollaborationPlugin
id={`note-${props.noteId}`}
providerFactory={providerFactory}
shouldBootstrap={false}
excludedProperties={excludedProperties}
/>
<ParagraphPlugin />
<SelectionPlugin />
<HeaderPlugin />
<FormattedTextPlugin />
<TaskPlugin />
<LinkPlugin />
<TermPlugin />
</LexicalComposer>
);
}
const excludedProperties = new Map<Klass<LexicalNode>, Set<string>>([
[HeaderNode, new Set(["__hasFocus"])],
[TermNode, new Set(["__hasFocus"])],
[TaskNode, new Set(["__hasFocus"])],
[LinkNode, new Set(["__hasFocus"])],
[FormattedTextNode, new Set(["__hasFocus"])],
])
const theme = {
};
const initialEditorConfig: InitialConfigType = {
namespace: "NoteEditor",
theme,
onError: console.error,
editorState: null,
nodes: [
HeaderNode, HeaderMarkerNode,
FormattedTextNode, FormattedTextMarkerNode,
TaskNode, TaskMarkerNode, TaskIconNode,
LinkNode, LinkMarkerNode, LinkUrlNode, LinkIconNode,
TermNode, TermMarkerNode, TermIconNode,
],
};