This reverts commit e9680cab74.
This commit is contained in:
parent
e9680cab74
commit
9408e92e44
65 changed files with 361 additions and 1469 deletions
|
|
@ -8,7 +8,7 @@ import { IssueWidget } from "@/extensions";
|
|||
// helpers
|
||||
import { getEditorClassNames } from "@/helpers/common";
|
||||
// hooks
|
||||
import { useCollaborativeDocumentEditor } from "@/hooks/use-collaborative-document-editor";
|
||||
import { useCollaborativeEditor } from "@/hooks/use-collaborative-editor";
|
||||
// types
|
||||
import { EditorRefApi, ICollaborativeDocumentEditor } from "@/types";
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
|
|||
}
|
||||
|
||||
// use document editor
|
||||
const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeDocumentEditor({
|
||||
const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeEditor({
|
||||
onTransaction,
|
||||
disabledExtensions,
|
||||
editorClassName,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { IssueWidget } from "@/extensions";
|
|||
// helpers
|
||||
import { getEditorClassNames } from "@/helpers/common";
|
||||
// hooks
|
||||
import { useCollaborativeDocumentReadOnlyEditor } from "@/hooks/use-collaborative-document-read-only-editor";
|
||||
import { useReadOnlyCollaborativeEditor } from "@/hooks/use-read-only-collaborative-editor";
|
||||
// types
|
||||
import { EditorReadOnlyRefApi, ICollaborativeDocumentReadOnlyEditor } from "@/types";
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ const CollaborativeDocumentReadOnlyEditor = (props: ICollaborativeDocumentReadOn
|
|||
);
|
||||
}
|
||||
|
||||
const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeDocumentReadOnlyEditor({
|
||||
const { editor, hasServerConnectionFailed, hasServerSynced } = useReadOnlyCollaborativeEditor({
|
||||
editorClassName,
|
||||
extensions,
|
||||
fileHandler,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { AnyExtension, Editor } from "@tiptap/core";
|
||||
import { Editor, Extension } from "@tiptap/core";
|
||||
// components
|
||||
import { EditorContainer } from "@/components/editors";
|
||||
// constants
|
||||
|
|
@ -12,7 +12,7 @@ import { EditorContentWrapper } from "./editor-content";
|
|||
|
||||
type Props = IEditorProps & {
|
||||
children?: (editor: Editor) => React.ReactNode;
|
||||
extensions: AnyExtension[];
|
||||
extensions: Extension<any, any>[];
|
||||
};
|
||||
|
||||
export const EditorWrapper: React.FC<Props> = (props) => {
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import React from "react";
|
||||
// components
|
||||
import { EditorContainer, EditorContentWrapper } from "@/components/editors";
|
||||
import { EditorBubbleMenu } from "@/components/menus";
|
||||
// constants
|
||||
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
|
||||
// helpers
|
||||
import { getEditorClassNames } from "@/helpers/common";
|
||||
// hooks
|
||||
import { useCollaborativeRichTextEditor } from "@/hooks/use-collaborative-rich-text-editor";
|
||||
// types
|
||||
import { EditorRefApi, ICollaborativeRichTextEditor } from "@/types";
|
||||
|
||||
const CollaborativeRichTextEditor = (props: ICollaborativeRichTextEditor) => {
|
||||
const {
|
||||
containerClassName,
|
||||
displayConfig = DEFAULT_DISPLAY_CONFIG,
|
||||
editorClassName,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
id,
|
||||
mentionHandler,
|
||||
onChange,
|
||||
placeholder,
|
||||
tabIndex,
|
||||
value,
|
||||
} = props;
|
||||
|
||||
const { editor } = useCollaborativeRichTextEditor({
|
||||
editorClassName,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
id,
|
||||
mentionHandler,
|
||||
onChange,
|
||||
placeholder,
|
||||
tabIndex,
|
||||
value,
|
||||
});
|
||||
|
||||
const editorContainerClassName = getEditorClassNames({
|
||||
noBorder: true,
|
||||
borderOnFocus: false,
|
||||
containerClassName,
|
||||
});
|
||||
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
<EditorContainer
|
||||
displayConfig={displayConfig}
|
||||
editor={editor}
|
||||
editorContainerClassName={editorContainerClassName}
|
||||
id={id}
|
||||
>
|
||||
<EditorBubbleMenu editor={editor} />
|
||||
<div className="flex flex-col">
|
||||
<EditorContentWrapper editor={editor} id={id} tabIndex={tabIndex} />
|
||||
</div>
|
||||
</EditorContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const CollaborativeRichTextEditorWithRef = React.forwardRef<EditorRefApi, ICollaborativeRichTextEditor>(
|
||||
(props, ref) => (
|
||||
<CollaborativeRichTextEditor {...props} forwardedRef={ref as React.MutableRefObject<EditorRefApi | null>} />
|
||||
)
|
||||
);
|
||||
|
||||
CollaborativeRichTextEditorWithRef.displayName = "CollaborativeRichTextEditorWithRef";
|
||||
|
||||
export { CollaborativeRichTextEditorWithRef };
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import React from "react";
|
||||
// components
|
||||
import { EditorContainer, EditorContentWrapper } from "@/components/editors";
|
||||
import { EditorBubbleMenu } from "@/components/menus";
|
||||
// constants
|
||||
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
|
||||
// helpers
|
||||
import { getEditorClassNames } from "@/helpers/common";
|
||||
// hooks
|
||||
import { useCollaborativeRichTextReadOnlyEditor } from "@/hooks/use-collaborative-rich-text-read-only-editor";
|
||||
// types
|
||||
import { EditorReadOnlyRefApi, ICollaborativeRichTextReadOnlyEditor } from "@/types";
|
||||
|
||||
const CollaborativeRichTextReadOnlyEditor = (props: ICollaborativeRichTextReadOnlyEditor) => {
|
||||
const {
|
||||
containerClassName,
|
||||
displayConfig = DEFAULT_DISPLAY_CONFIG,
|
||||
editorClassName,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
id,
|
||||
mentionHandler,
|
||||
value,
|
||||
} = props;
|
||||
|
||||
const { editor } = useCollaborativeRichTextReadOnlyEditor({
|
||||
editorClassName,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
id,
|
||||
mentionHandler,
|
||||
value,
|
||||
});
|
||||
|
||||
const editorContainerClassName = getEditorClassNames({
|
||||
noBorder: true,
|
||||
borderOnFocus: false,
|
||||
containerClassName,
|
||||
});
|
||||
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
<EditorContainer
|
||||
displayConfig={displayConfig}
|
||||
editor={editor}
|
||||
editorContainerClassName={editorContainerClassName}
|
||||
id={id}
|
||||
>
|
||||
<EditorBubbleMenu editor={editor} />
|
||||
<div className="flex flex-col">
|
||||
<EditorContentWrapper editor={editor} id={id} />
|
||||
</div>
|
||||
</EditorContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const CollaborativeRichTextReadOnlyEditorWithRef = React.forwardRef<
|
||||
EditorReadOnlyRefApi,
|
||||
ICollaborativeRichTextReadOnlyEditor
|
||||
>((props, ref) => (
|
||||
<CollaborativeRichTextReadOnlyEditor
|
||||
{...props}
|
||||
forwardedRef={ref as React.MutableRefObject<EditorReadOnlyRefApi | null>}
|
||||
/>
|
||||
));
|
||||
|
||||
CollaborativeRichTextReadOnlyEditorWithRef.displayName = "CollaborativeRichTextReadOnlyEditorWithRef";
|
||||
|
||||
export { CollaborativeRichTextReadOnlyEditorWithRef };
|
||||
|
|
@ -1,4 +1,2 @@
|
|||
export * from "./collaborative-editor";
|
||||
export * from "./collaborative-read-only-editor";
|
||||
export * from "./editor";
|
||||
export * from "./read-only-editor";
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
import { CoreEditorExtensionsWithoutProps, DocumentEditorExtensionsWithoutProps } from "@/extensions";
|
||||
import { getSchema } from "@tiptap/core";
|
||||
import { generateHTML, generateJSON } from "@tiptap/html";
|
||||
import { prosemirrorJSONToYDoc, yXmlFragmentToProseMirrorRootNode } from "y-prosemirror";
|
||||
import * as Y from "yjs";
|
||||
|
||||
// editor extension configs
|
||||
const RICH_TEXT_EDITOR_EXTENSIONS = CoreEditorExtensionsWithoutProps;
|
||||
const DOCUMENT_EDITOR_EXTENSIONS = [...CoreEditorExtensionsWithoutProps, ...DocumentEditorExtensionsWithoutProps];
|
||||
// editor schemas
|
||||
const richTextEditorSchema = getSchema(RICH_TEXT_EDITOR_EXTENSIONS);
|
||||
const documentEditorSchema = getSchema(DOCUMENT_EDITOR_EXTENSIONS);
|
||||
|
||||
/**
|
||||
* @description apply updates to a doc and return the updated doc in binary format
|
||||
* @param {Uint8Array} document
|
||||
* @param {Uint8Array} updates
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export const applyUpdates = (document: Uint8Array, updates?: Uint8Array): Uint8Array => {
|
||||
const yDoc = new Y.Doc();
|
||||
Y.applyUpdate(yDoc, document);
|
||||
if (updates) {
|
||||
Y.applyUpdate(yDoc, updates);
|
||||
}
|
||||
|
||||
const encodedDoc = Y.encodeStateAsUpdate(yDoc);
|
||||
return encodedDoc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description this function encodes binary data to base64 string
|
||||
* @param {Uint8Array} document
|
||||
* @returns {string}
|
||||
*/
|
||||
export const convertBinaryDataToBase64String = (document: Uint8Array): string =>
|
||||
Buffer.from(document).toString("base64");
|
||||
|
||||
/**
|
||||
* @description this function decodes base64 string to binary data
|
||||
* @param {string} document
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
export const convertBase64StringToBinaryData = (document: string): ArrayBuffer => Buffer.from(document, "base64");
|
||||
|
||||
/**
|
||||
* @description this function generates the binary equivalent of html content for the rich text editor
|
||||
* @param {string} descriptionHTML
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export const getBinaryDataFromRichTextEditorHTMLString = (descriptionHTML: string): Uint8Array => {
|
||||
// convert HTML to JSON
|
||||
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", RICH_TEXT_EDITOR_EXTENSIONS);
|
||||
// convert JSON to Y.Doc format
|
||||
const transformedData = prosemirrorJSONToYDoc(richTextEditorSchema, contentJSON, "default");
|
||||
// convert Y.Doc to Uint8Array format
|
||||
const encodedData = Y.encodeStateAsUpdate(transformedData);
|
||||
return encodedData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description this function generates the binary equivalent of html content for the document editor
|
||||
* @param {string} descriptionHTML
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export const getBinaryDataFromDocumentEditorHTMLString = (descriptionHTML: string): Uint8Array => {
|
||||
// convert HTML to JSON
|
||||
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", DOCUMENT_EDITOR_EXTENSIONS);
|
||||
// convert JSON to Y.Doc format
|
||||
const transformedData = prosemirrorJSONToYDoc(documentEditorSchema, contentJSON, "default");
|
||||
// convert Y.Doc to Uint8Array format
|
||||
const encodedData = Y.encodeStateAsUpdate(transformedData);
|
||||
return encodedData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description this function generates all document formats for the provided binary data for the rich text editor
|
||||
* @param {Uint8Array} description
|
||||
* @returns
|
||||
*/
|
||||
export const getAllDocumentFormatsFromRichTextEditorBinaryData = (
|
||||
description: Uint8Array
|
||||
): {
|
||||
contentBinaryEncoded: string;
|
||||
contentJSON: object;
|
||||
contentHTML: string;
|
||||
} => {
|
||||
// encode binary description data
|
||||
const base64Data = convertBinaryDataToBase64String(description);
|
||||
const yDoc = new Y.Doc();
|
||||
Y.applyUpdate(yDoc, description);
|
||||
// convert to JSON
|
||||
const type = yDoc.getXmlFragment("default");
|
||||
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, richTextEditorSchema).toJSON();
|
||||
// convert to HTML
|
||||
const contentHTML = generateHTML(contentJSON, RICH_TEXT_EDITOR_EXTENSIONS);
|
||||
|
||||
return {
|
||||
contentBinaryEncoded: base64Data,
|
||||
contentJSON,
|
||||
contentHTML,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @description this function generates all document formats for the provided binary data for the document editor
|
||||
* @param {Uint8Array} description
|
||||
* @returns
|
||||
*/
|
||||
export const getAllDocumentFormatsFromDocumentEditorBinaryData = (
|
||||
description: Uint8Array
|
||||
): {
|
||||
contentBinaryEncoded: string;
|
||||
contentJSON: object;
|
||||
contentHTML: string;
|
||||
} => {
|
||||
// encode binary description data
|
||||
const base64Data = convertBinaryDataToBase64String(description);
|
||||
const yDoc = new Y.Doc();
|
||||
Y.applyUpdate(yDoc, description);
|
||||
// convert to JSON
|
||||
const type = yDoc.getXmlFragment("default");
|
||||
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, documentEditorSchema).toJSON();
|
||||
// convert to HTML
|
||||
const contentHTML = generateHTML(contentJSON, DOCUMENT_EDITOR_EXTENSIONS);
|
||||
|
||||
return {
|
||||
contentBinaryEncoded: base64Data,
|
||||
contentJSON,
|
||||
contentHTML,
|
||||
};
|
||||
};
|
||||
16
packages/editor/src/core/helpers/yjs.ts
Normal file
16
packages/editor/src/core/helpers/yjs.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import * as Y from "yjs";
|
||||
|
||||
/**
|
||||
* @description apply updates to a doc and return the updated doc in base64(binary) format
|
||||
* @param {Uint8Array} document
|
||||
* @param {Uint8Array} updates
|
||||
* @returns {string} base64(binary) form of the updated doc
|
||||
*/
|
||||
export const applyUpdates = (document: Uint8Array, updates: Uint8Array): Uint8Array => {
|
||||
const yDoc = new Y.Doc();
|
||||
Y.applyUpdate(yDoc, document);
|
||||
Y.applyUpdate(yDoc, updates);
|
||||
|
||||
const encodedDoc = Y.encodeStateAsUpdate(yDoc);
|
||||
return encodedDoc;
|
||||
};
|
||||
|
|
@ -9,9 +9,9 @@ import { useEditor } from "@/hooks/use-editor";
|
|||
// plane editor extensions
|
||||
import { DocumentEditorAdditionalExtensions } from "@/plane-editor/extensions";
|
||||
// types
|
||||
import { TCollaborativeDocumentEditorHookProps } from "@/types";
|
||||
import { TCollaborativeEditorProps } from "@/types";
|
||||
|
||||
export const useCollaborativeDocumentEditor = (props: TCollaborativeDocumentEditorHookProps) => {
|
||||
export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
|
||||
const {
|
||||
onTransaction,
|
||||
disabledExtensions,
|
||||
|
|
@ -102,7 +102,7 @@ export const useCollaborativeDocumentEditor = (props: TCollaborativeDocumentEdit
|
|||
forwardedRef,
|
||||
mentionHandler,
|
||||
placeholder,
|
||||
providerDocument: provider.document,
|
||||
provider,
|
||||
tabIndex,
|
||||
});
|
||||
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
import { useEffect, useMemo } from "react";
|
||||
import Collaboration from "@tiptap/extension-collaboration";
|
||||
import * as Y from "yjs";
|
||||
// extensions
|
||||
import { HeadingListExtension, SideMenuExtension } from "@/extensions";
|
||||
// hooks
|
||||
import { useEditor } from "@/hooks/use-editor";
|
||||
// providers
|
||||
import { CustomCollaborationProvider } from "@/providers";
|
||||
// types
|
||||
import { TCollaborativeRichTextEditorHookProps } from "@/types";
|
||||
|
||||
export const useCollaborativeRichTextEditor = (props: TCollaborativeRichTextEditorHookProps) => {
|
||||
const {
|
||||
editorClassName,
|
||||
editorProps = {},
|
||||
extensions,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
handleEditorReady,
|
||||
id,
|
||||
mentionHandler,
|
||||
onChange,
|
||||
placeholder,
|
||||
tabIndex,
|
||||
value,
|
||||
} = props;
|
||||
// initialize custom collaboration provider
|
||||
const provider = useMemo(
|
||||
() =>
|
||||
new CustomCollaborationProvider({
|
||||
name: id,
|
||||
onChange,
|
||||
}),
|
||||
[id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (provider.hasSynced) return;
|
||||
if (value && value.length > 0) {
|
||||
try {
|
||||
Y.applyUpdate(provider.document, value);
|
||||
provider.hasSynced = true;
|
||||
} catch (error) {
|
||||
console.error("Error applying binary updates to the description", error);
|
||||
}
|
||||
}
|
||||
}, [value, provider.document]);
|
||||
|
||||
const editor = useEditor({
|
||||
id,
|
||||
editorProps,
|
||||
editorClassName,
|
||||
enableHistory: false,
|
||||
extensions: [
|
||||
SideMenuExtension({
|
||||
aiEnabled: false,
|
||||
dragDropEnabled: true,
|
||||
}),
|
||||
HeadingListExtension,
|
||||
Collaboration.configure({
|
||||
document: provider.document,
|
||||
}),
|
||||
...(extensions ?? []),
|
||||
],
|
||||
fileHandler,
|
||||
handleEditorReady,
|
||||
forwardedRef,
|
||||
mentionHandler,
|
||||
placeholder,
|
||||
providerDocument: provider.document,
|
||||
tabIndex,
|
||||
});
|
||||
|
||||
return {
|
||||
editor,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import { useEffect, useMemo } from "react";
|
||||
import Collaboration from "@tiptap/extension-collaboration";
|
||||
import * as Y from "yjs";
|
||||
// extensions
|
||||
import { HeadingListExtension, SideMenuExtension } from "@/extensions";
|
||||
// hooks
|
||||
import { useReadOnlyEditor } from "@/hooks/use-read-only-editor";
|
||||
// providers
|
||||
import { CustomCollaborationProvider } from "@/providers";
|
||||
// types
|
||||
import { TCollaborativeRichTextReadOnlyEditorHookProps } from "@/types";
|
||||
|
||||
export const useCollaborativeRichTextReadOnlyEditor = (props: TCollaborativeRichTextReadOnlyEditorHookProps) => {
|
||||
const {
|
||||
editorClassName,
|
||||
editorProps = {},
|
||||
extensions,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
handleEditorReady,
|
||||
id,
|
||||
mentionHandler,
|
||||
value,
|
||||
} = props;
|
||||
// initialize custom collaboration provider
|
||||
const provider = useMemo(
|
||||
() =>
|
||||
new CustomCollaborationProvider({
|
||||
name: id,
|
||||
}),
|
||||
[id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value.length > 0) {
|
||||
Y.applyUpdate(provider.document, value);
|
||||
}
|
||||
}, [value, provider.document]);
|
||||
|
||||
const editor = useReadOnlyEditor({
|
||||
editorProps,
|
||||
editorClassName,
|
||||
extensions: [
|
||||
SideMenuExtension({
|
||||
aiEnabled: false,
|
||||
dragDropEnabled: true,
|
||||
}),
|
||||
HeadingListExtension,
|
||||
Collaboration.configure({
|
||||
document: provider.document,
|
||||
}),
|
||||
...(extensions ?? []),
|
||||
],
|
||||
fileHandler,
|
||||
handleEditorReady,
|
||||
forwardedRef,
|
||||
mentionHandler,
|
||||
providerDocument: provider.document,
|
||||
});
|
||||
|
||||
return {
|
||||
editor,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { useImperativeHandle, useRef, MutableRefObject, useState, useEffect } from "react";
|
||||
import { HocuspocusProvider } from "@hocuspocus/provider";
|
||||
import { DOMSerializer } from "@tiptap/pm/model";
|
||||
import { Selection } from "@tiptap/pm/state";
|
||||
import { EditorProps } from "@tiptap/pm/view";
|
||||
|
|
@ -35,7 +36,7 @@ export interface CustomEditorProps {
|
|||
onTransaction?: () => void;
|
||||
autofocus?: boolean;
|
||||
placeholder?: string | ((isFocused: boolean, value: string) => string);
|
||||
providerDocument?: Y.Doc;
|
||||
provider?: HocuspocusProvider;
|
||||
tabIndex?: number;
|
||||
// undefined when prop is not passed, null if intentionally passed to stop
|
||||
// swr syncing
|
||||
|
|
@ -57,7 +58,7 @@ export const useEditor = (props: CustomEditorProps) => {
|
|||
onChange,
|
||||
onTransaction,
|
||||
placeholder,
|
||||
providerDocument,
|
||||
provider,
|
||||
tabIndex,
|
||||
value,
|
||||
autofocus = false,
|
||||
|
|
@ -205,7 +206,7 @@ export const useEditor = (props: CustomEditorProps) => {
|
|||
return markdownOutput;
|
||||
},
|
||||
getDocument: () => {
|
||||
const documentBinary = providerDocument ? Y.encodeStateAsUpdate(providerDocument) : null;
|
||||
const documentBinary = provider?.document ? Y.encodeStateAsUpdate(provider?.document) : null;
|
||||
const documentHTML = editorRef.current?.getHTML() ?? "<p></p>";
|
||||
const documentJSON = editorRef.current?.getJSON() ?? null;
|
||||
|
||||
|
|
@ -283,7 +284,7 @@ export const useEditor = (props: CustomEditorProps) => {
|
|||
words: editorRef?.current?.storage?.characterCount?.words?.() ?? 0,
|
||||
}),
|
||||
setProviderDocument: (value) => {
|
||||
const document = providerDocument;
|
||||
const document = provider?.document;
|
||||
if (!document) return;
|
||||
Y.applyUpdate(document, value);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { HeadingListExtension } from "@/extensions";
|
|||
// hooks
|
||||
import { useReadOnlyEditor } from "@/hooks/use-read-only-editor";
|
||||
// types
|
||||
import { TCollaborativeDocumentReadOnlyEditorHookProps } from "@/types";
|
||||
import { TReadOnlyCollaborativeEditorProps } from "@/types";
|
||||
|
||||
export const useCollaborativeDocumentReadOnlyEditor = (props: TCollaborativeDocumentReadOnlyEditorHookProps) => {
|
||||
export const useReadOnlyCollaborativeEditor = (props: TReadOnlyCollaborativeEditorProps) => {
|
||||
const {
|
||||
editorClassName,
|
||||
editorProps = {},
|
||||
|
|
@ -79,7 +79,7 @@ export const useCollaborativeDocumentReadOnlyEditor = (props: TCollaborativeDocu
|
|||
forwardedRef,
|
||||
handleEditorReady,
|
||||
mentionHandler,
|
||||
providerDocument: provider.document,
|
||||
provider,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { useImperativeHandle, useRef, MutableRefObject, useEffect } from "react";
|
||||
import { HocuspocusProvider } from "@hocuspocus/provider";
|
||||
import { EditorProps } from "@tiptap/pm/view";
|
||||
import { useEditor as useCustomEditor, Editor } from "@tiptap/react";
|
||||
import * as Y from "yjs";
|
||||
|
|
@ -23,7 +24,7 @@ interface CustomReadOnlyEditorProps {
|
|||
mentionHandler: {
|
||||
highlights: () => Promise<IMentionHighlight[]>;
|
||||
};
|
||||
providerDocument?: Y.Doc;
|
||||
provider?: HocuspocusProvider;
|
||||
}
|
||||
|
||||
export const useReadOnlyEditor = (props: CustomReadOnlyEditorProps) => {
|
||||
|
|
@ -36,7 +37,7 @@ export const useReadOnlyEditor = (props: CustomReadOnlyEditorProps) => {
|
|||
fileHandler,
|
||||
handleEditorReady,
|
||||
mentionHandler,
|
||||
providerDocument,
|
||||
provider,
|
||||
} = props;
|
||||
|
||||
const editor = useCustomEditor({
|
||||
|
|
@ -85,7 +86,7 @@ export const useReadOnlyEditor = (props: CustomReadOnlyEditorProps) => {
|
|||
return markdownOutput;
|
||||
},
|
||||
getDocument: () => {
|
||||
const documentBinary = providerDocument ? Y.encodeStateAsUpdate(providerDocument) : null;
|
||||
const documentBinary = provider?.document ? Y.encodeStateAsUpdate(provider?.document) : null;
|
||||
const documentHTML = editorRef.current?.getHTML() ?? "<p></p>";
|
||||
const documentJSON = editorRef.current?.getJSON() ?? null;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
import * as Y from "yjs";
|
||||
|
||||
export interface CompleteCollaborationProviderConfiguration {
|
||||
/**
|
||||
* The identifier/name of your document
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The actual Y.js document
|
||||
*/
|
||||
document: Y.Doc;
|
||||
/**
|
||||
* onChange callback
|
||||
*/
|
||||
onChange: (updates: Uint8Array) => void;
|
||||
}
|
||||
|
||||
export type CollaborationProviderConfiguration = Required<Pick<CompleteCollaborationProviderConfiguration, "name">> &
|
||||
Partial<CompleteCollaborationProviderConfiguration>;
|
||||
|
||||
export class CustomCollaborationProvider {
|
||||
public hasSynced: boolean;
|
||||
|
||||
public configuration: CompleteCollaborationProviderConfiguration = {
|
||||
name: "",
|
||||
document: new Y.Doc(),
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
constructor(configuration: CollaborationProviderConfiguration) {
|
||||
this.hasSynced = false;
|
||||
this.setConfiguration(configuration);
|
||||
this.document.on("update", this.documentUpdateHandler.bind(this));
|
||||
this.document.on("destroy", this.documentDestroyHandler.bind(this));
|
||||
}
|
||||
|
||||
public setConfiguration(configuration: Partial<CompleteCollaborationProviderConfiguration> = {}): void {
|
||||
this.configuration = {
|
||||
...this.configuration,
|
||||
...configuration,
|
||||
};
|
||||
}
|
||||
|
||||
get document() {
|
||||
return this.configuration.document;
|
||||
}
|
||||
|
||||
async documentUpdateHandler(_update: Uint8Array, origin: any) {
|
||||
if (!this.hasSynced) return;
|
||||
// return if the update is from the provider itself
|
||||
if (origin === this) return;
|
||||
// call onChange with the update
|
||||
const stateVector = Y.encodeStateAsUpdate(this.document);
|
||||
this.configuration.onChange?.(stateVector);
|
||||
}
|
||||
|
||||
documentDestroyHandler() {
|
||||
this.document.off("update", this.documentUpdateHandler);
|
||||
this.document.off("destroy", this.documentDestroyHandler);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from "./custom-collaboration-provider";
|
||||
|
|
@ -19,7 +19,7 @@ export type TServerHandler = {
|
|||
onServerError?: () => void;
|
||||
};
|
||||
|
||||
type TCollaborativeEditorHookCommonProps = {
|
||||
type TCollaborativeEditorHookProps = {
|
||||
disabledExtensions?: TExtensions[];
|
||||
editorClassName: string;
|
||||
editorProps?: EditorProps;
|
||||
|
|
@ -30,9 +30,12 @@ type TCollaborativeEditorHookCommonProps = {
|
|||
highlights: () => Promise<IMentionHighlight[]>;
|
||||
suggestions?: () => Promise<IMentionSuggestion[]>;
|
||||
};
|
||||
realtimeConfig: TRealtimeConfig;
|
||||
serverHandler?: TServerHandler;
|
||||
user: TUserDetails;
|
||||
};
|
||||
|
||||
type TCollaborativeEditorHookProps = TCollaborativeEditorHookCommonProps & {
|
||||
export type TCollaborativeEditorProps = TCollaborativeEditorHookProps & {
|
||||
onTransaction?: () => void;
|
||||
embedHandler?: TEmbedConfig;
|
||||
fileHandler: TFileHandler;
|
||||
|
|
@ -41,29 +44,7 @@ type TCollaborativeEditorHookProps = TCollaborativeEditorHookCommonProps & {
|
|||
tabIndex?: number;
|
||||
};
|
||||
|
||||
type TCollaborativeReadOnlyEditorHookProps = TCollaborativeEditorHookCommonProps & {
|
||||
export type TReadOnlyCollaborativeEditorProps = TCollaborativeEditorHookProps & {
|
||||
fileHandler: Pick<TFileHandler, "getAssetSrc">;
|
||||
forwardedRef?: React.MutableRefObject<EditorReadOnlyRefApi | null>;
|
||||
};
|
||||
|
||||
export type TCollaborativeRichTextEditorHookProps = TCollaborativeEditorHookProps & {
|
||||
onChange: (updatedDescription: Uint8Array) => void;
|
||||
value: Uint8Array;
|
||||
};
|
||||
|
||||
export type TCollaborativeRichTextReadOnlyEditorHookProps = TCollaborativeReadOnlyEditorHookProps & {
|
||||
value: Uint8Array;
|
||||
};
|
||||
|
||||
export type TCollaborativeDocumentEditorHookProps = TCollaborativeEditorHookProps & {
|
||||
embedHandler?: TEmbedConfig;
|
||||
realtimeConfig: TRealtimeConfig;
|
||||
serverHandler?: TServerHandler;
|
||||
user: TUserDetails;
|
||||
};
|
||||
|
||||
export type TCollaborativeDocumentReadOnlyEditorHookProps = TCollaborativeReadOnlyEditorHookProps & {
|
||||
realtimeConfig: TRealtimeConfig;
|
||||
serverHandler?: TServerHandler;
|
||||
user: TUserDetails;
|
||||
};
|
||||
|
|
@ -132,12 +132,6 @@ export interface IRichTextEditor extends IEditorProps {
|
|||
dragDropEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ICollaborativeRichTextEditor extends Omit<IEditorProps, "initialValue" | "onChange" | "value"> {
|
||||
dragDropEnabled?: boolean;
|
||||
onChange: (updatedDescription: Uint8Array) => void;
|
||||
value: Uint8Array;
|
||||
}
|
||||
|
||||
export interface ICollaborativeDocumentEditor
|
||||
extends Omit<IEditorProps, "initialValue" | "onChange" | "onEnterKeyPress" | "value"> {
|
||||
aiHandler?: TAIHandler;
|
||||
|
|
@ -167,10 +161,6 @@ export type ILiteTextReadOnlyEditor = IReadOnlyEditorProps;
|
|||
|
||||
export type IRichTextReadOnlyEditor = IReadOnlyEditorProps;
|
||||
|
||||
export type ICollaborativeRichTextReadOnlyEditor = Omit<IReadOnlyEditorProps, "initialValue"> & {
|
||||
value: Uint8Array;
|
||||
};
|
||||
|
||||
export interface ICollaborativeDocumentReadOnlyEditor extends Omit<IReadOnlyEditorProps, "initialValue"> {
|
||||
embedHandler: TEmbedConfig;
|
||||
handleEditorReady?: (value: boolean) => void;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
export * from "./ai";
|
||||
export * from "./collaboration-hook";
|
||||
export * from "./collaboration";
|
||||
export * from "./config";
|
||||
export * from "./editor";
|
||||
export * from "./embed";
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ import "./styles/drag-drop.css";
|
|||
export {
|
||||
CollaborativeDocumentEditorWithRef,
|
||||
CollaborativeDocumentReadOnlyEditorWithRef,
|
||||
CollaborativeRichTextEditorWithRef,
|
||||
CollaborativeRichTextReadOnlyEditorWithRef,
|
||||
DocumentReadOnlyEditorWithRef,
|
||||
LiteTextEditorWithRef,
|
||||
LiteTextReadOnlyEditorWithRef,
|
||||
|
|
@ -27,7 +25,7 @@ export * from "@/constants/common";
|
|||
// helpers
|
||||
export * from "@/helpers/common";
|
||||
export * from "@/helpers/editor-commands";
|
||||
export * from "@/helpers/yjs-utils";
|
||||
export * from "@/helpers/yjs";
|
||||
export * from "@/extensions/table/table";
|
||||
|
||||
// components
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export * from "@/helpers/yjs-utils";
|
||||
export * from "@/extensions/core-without-props";
|
||||
|
|
|
|||
1
packages/types/src/issues/issue.d.ts
vendored
1
packages/types/src/issues/issue.d.ts
vendored
|
|
@ -50,7 +50,6 @@ export type IssueRelation = {
|
|||
};
|
||||
|
||||
export type TIssue = TBaseIssue & {
|
||||
description_binary?: string;
|
||||
description_html?: string;
|
||||
is_subscribed?: boolean;
|
||||
parent?: Partial<TBaseIssue>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue