90 lines
3.5 KiB
TypeScript
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,
|
|
],
|
|
};
|
|
|