mirror of
https://github.com/docmost/docmost.git
synced 2026-05-17 23:14:07 +08:00
Tiptap3 migration - WIP
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
import {
|
||||
BubbleMenu,
|
||||
BubbleMenuProps,
|
||||
isNodeSelection,
|
||||
useEditor,
|
||||
} from "@tiptap/react";
|
||||
import { BubbleMenu, BubbleMenuProps } from "@tiptap/react/menus";
|
||||
import { isNodeSelection, useEditor } from "@tiptap/react";
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
IconBold,
|
||||
@@ -114,14 +110,9 @@ export const EditorBubbleMenu: FC<EditorBubbleMenuProps> = (props) => {
|
||||
}
|
||||
return isTextSelected(editor);
|
||||
},
|
||||
tippyOptions: {
|
||||
moveTransition: "transform 0.15s ease-out",
|
||||
onCreate: (instance) => {
|
||||
instance.popper.firstChild?.addEventListener("blur", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
});
|
||||
},
|
||||
options: {
|
||||
placement: "top",
|
||||
offset: 8,
|
||||
onHide: () => {
|
||||
setIsNodeSelectorOpen(false);
|
||||
setIsTextAlignmentOpen(false);
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
BubbleMenu as BaseBubbleMenu,
|
||||
findParentNode,
|
||||
posToDOMRect,
|
||||
} from "@tiptap/react";
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { findParentNode, posToDOMRect } from "@tiptap/react";
|
||||
import React, { useCallback } from "react";
|
||||
import { Node as PMNode } from "prosemirror-model";
|
||||
import {
|
||||
@@ -55,20 +52,17 @@ export function CalloutMenu({ editor }: EditorMenuProps) {
|
||||
},
|
||||
[editor],
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseBubbleMenu
|
||||
editor={editor}
|
||||
pluginKey={`callout-menu}`}
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
getReferenceClientRect,
|
||||
offset: [0, 10],
|
||||
placement: "bottom",
|
||||
zIndex: 99,
|
||||
popperOptions: {
|
||||
modifiers: [{ name: "flip", enabled: false }],
|
||||
},
|
||||
options={{
|
||||
// getReferenceClientRect,
|
||||
placement: "right-end",
|
||||
// offset: 233,
|
||||
// zIndex: 99,
|
||||
flip: false,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
|
||||
@@ -90,6 +90,7 @@ export default function CodeBlockView(props: NodeViewProps) {
|
||||
node.textContent.length > 0
|
||||
}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<NodeViewContent as="code" className={`language-${language}`} />
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import {
|
||||
BubbleMenu as BaseBubbleMenu,
|
||||
findParentNode,
|
||||
posToDOMRect,
|
||||
} from '@tiptap/react';
|
||||
import { useCallback } from 'react';
|
||||
import { sticky } from 'tippy.js';
|
||||
import { Node as PMNode } from 'prosemirror-model';
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { findParentNode, posToDOMRect } from "@tiptap/react";
|
||||
import { useCallback } from "react";
|
||||
import { Node as PMNode } from "prosemirror-model";
|
||||
import {
|
||||
EditorMenuProps,
|
||||
ShouldShowProps,
|
||||
} from '@/features/editor/components/table/types/types.ts';
|
||||
import { NodeWidthResize } from '@/features/editor/components/common/node-width-resize.tsx';
|
||||
} from "@/features/editor/components/table/types/types.ts";
|
||||
import { NodeWidthResize } from "@/features/editor/components/common/node-width-resize.tsx";
|
||||
|
||||
export function DrawioMenu({ editor }: EditorMenuProps) {
|
||||
const shouldShow = useCallback(
|
||||
@@ -19,14 +15,14 @@ export function DrawioMenu({ editor }: EditorMenuProps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return editor.isActive('drawio') && editor.getAttributes('drawio')?.src;
|
||||
return editor.isActive("drawio") && editor.getAttributes("drawio")?.src;
|
||||
},
|
||||
[editor]
|
||||
[editor],
|
||||
);
|
||||
|
||||
const getReferenceClientRect = useCallback(() => {
|
||||
const { selection } = editor.state;
|
||||
const predicate = (node: PMNode) => node.type.name === 'drawio';
|
||||
const predicate = (node: PMNode) => node.type.name === "drawio";
|
||||
const parent = findParentNode(predicate)(selection);
|
||||
|
||||
if (parent) {
|
||||
@@ -39,9 +35,9 @@ export function DrawioMenu({ editor }: EditorMenuProps) {
|
||||
|
||||
const onWidthChange = useCallback(
|
||||
(value: number) => {
|
||||
editor.commands.updateAttributes('drawio', { width: `${value}%` });
|
||||
editor.commands.updateAttributes("drawio", { width: `${value}%` });
|
||||
},
|
||||
[editor]
|
||||
[editor],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -49,29 +45,26 @@ export function DrawioMenu({ editor }: EditorMenuProps) {
|
||||
editor={editor}
|
||||
pluginKey={`drawio-menu}`}
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
getReferenceClientRect,
|
||||
offset: [0, 8],
|
||||
zIndex: 99,
|
||||
popperOptions: {
|
||||
modifiers: [{ name: 'flip', enabled: false }],
|
||||
},
|
||||
plugins: [sticky],
|
||||
sticky: 'popper',
|
||||
options={{
|
||||
//getReferenceClientRect,
|
||||
placement: "bottom",
|
||||
offset: 8,
|
||||
// zIndex: 99,
|
||||
flip: false,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{editor.getAttributes('drawio')?.width && (
|
||||
{editor.getAttributes("drawio")?.width && (
|
||||
<NodeWidthResize
|
||||
onChange={onWidthChange}
|
||||
value={parseInt(editor.getAttributes('drawio').width)}
|
||||
value={parseInt(editor.getAttributes("drawio").width)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -66,6 +66,7 @@ export default function DrawioView(props: NodeViewProps) {
|
||||
const fileName = "diagram.drawio.svg";
|
||||
const drawioSVGFile = await svgStringToFile(svgString, fileName);
|
||||
|
||||
//@ts-ignore
|
||||
const pageId = editor.storage?.pageId;
|
||||
|
||||
let attachment: IAttachment = null;
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import {
|
||||
BubbleMenu as BaseBubbleMenu,
|
||||
findParentNode,
|
||||
posToDOMRect,
|
||||
} from '@tiptap/react';
|
||||
import { useCallback } from 'react';
|
||||
import { sticky } from 'tippy.js';
|
||||
import { Node as PMNode } from 'prosemirror-model';
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { findParentNode, posToDOMRect } from "@tiptap/react";
|
||||
import { useCallback } from "react";
|
||||
import { Node as PMNode } from "prosemirror-model";
|
||||
import {
|
||||
EditorMenuProps,
|
||||
ShouldShowProps,
|
||||
} from '@/features/editor/components/table/types/types.ts';
|
||||
import { NodeWidthResize } from '@/features/editor/components/common/node-width-resize.tsx';
|
||||
} from "@/features/editor/components/table/types/types.ts";
|
||||
import { NodeWidthResize } from "@/features/editor/components/common/node-width-resize.tsx";
|
||||
|
||||
export function ExcalidrawMenu({ editor }: EditorMenuProps) {
|
||||
const shouldShow = useCallback(
|
||||
@@ -19,14 +15,16 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return editor.isActive('excalidraw') && editor.getAttributes('excalidraw')?.src;
|
||||
return (
|
||||
editor.isActive("excalidraw") && editor.getAttributes("excalidraw")?.src
|
||||
);
|
||||
},
|
||||
[editor]
|
||||
[editor],
|
||||
);
|
||||
|
||||
const getReferenceClientRect = useCallback(() => {
|
||||
const { selection } = editor.state;
|
||||
const predicate = (node: PMNode) => node.type.name === 'excalidraw';
|
||||
const predicate = (node: PMNode) => node.type.name === "excalidraw";
|
||||
const parent = findParentNode(predicate)(selection);
|
||||
|
||||
if (parent) {
|
||||
@@ -39,9 +37,9 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) {
|
||||
|
||||
const onWidthChange = useCallback(
|
||||
(value: number) => {
|
||||
editor.commands.updateAttributes('excalidraw', { width: `${value}%` });
|
||||
editor.commands.updateAttributes("excalidraw", { width: `${value}%` });
|
||||
},
|
||||
[editor]
|
||||
[editor],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -49,29 +47,26 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) {
|
||||
editor={editor}
|
||||
pluginKey={`excalidraw-menu}`}
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
getReferenceClientRect,
|
||||
offset: [0, 8],
|
||||
zIndex: 99,
|
||||
popperOptions: {
|
||||
modifiers: [{ name: 'flip', enabled: false }],
|
||||
},
|
||||
plugins: [sticky],
|
||||
sticky: 'popper',
|
||||
options={{
|
||||
//getReferenceClientRect,
|
||||
placement: "bottom",
|
||||
offset: 8,
|
||||
// zIndex: 99,
|
||||
flip: false,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{editor.getAttributes('excalidraw')?.width && (
|
||||
{editor.getAttributes("excalidraw")?.width && (
|
||||
<NodeWidthResize
|
||||
onChange={onWidthChange}
|
||||
value={parseInt(editor.getAttributes('excalidraw').width)}
|
||||
value={parseInt(editor.getAttributes("excalidraw").width)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -98,6 +98,7 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
const fileName = "diagram.excalidraw.svg";
|
||||
const excalidrawSvgFile = await svgStringToFile(svgString, fileName);
|
||||
|
||||
// @ts-ignore
|
||||
const pageId = editor.storage?.pageId;
|
||||
|
||||
let attachment: IAttachment = null;
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import {
|
||||
BubbleMenu as BaseBubbleMenu,
|
||||
findParentNode,
|
||||
posToDOMRect,
|
||||
} from "@tiptap/react";
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { findParentNode, posToDOMRect } from "@tiptap/react";
|
||||
import React, { useCallback } from "react";
|
||||
import { sticky } from "tippy.js";
|
||||
import { Node as PMNode } from "prosemirror-model";
|
||||
import {
|
||||
EditorMenuProps,
|
||||
@@ -85,15 +81,12 @@ export function ImageMenu({ editor }: EditorMenuProps) {
|
||||
editor={editor}
|
||||
pluginKey={`image-menu}`}
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
getReferenceClientRect,
|
||||
offset: [0, 8],
|
||||
zIndex: 99,
|
||||
popperOptions: {
|
||||
modifiers: [{ name: "flip", enabled: false }],
|
||||
},
|
||||
plugins: [sticky],
|
||||
sticky: "popper",
|
||||
options={{
|
||||
// getReferenceClientRect,
|
||||
placement: "bottom",
|
||||
offset: 8,
|
||||
//zIndex: 99,
|
||||
flip: false,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react";
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { offset } from "@floating-ui/dom";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { EditorMenuProps } from "@/features/editor/components/table/types/types.ts";
|
||||
import { LinkEditorPanel } from "@/features/editor/components/link/link-editor-panel.tsx";
|
||||
@@ -50,16 +51,13 @@ export function LinkMenu({ editor, appendTo }: EditorMenuProps) {
|
||||
editor={editor}
|
||||
pluginKey={`link-menu}`}
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
appendTo: () => {
|
||||
return appendTo?.current;
|
||||
},
|
||||
onHidden: () => {
|
||||
options={{
|
||||
onHide: () => {
|
||||
setShowEdit(false);
|
||||
},
|
||||
placement: "bottom",
|
||||
offset: [0, 5],
|
||||
zIndex: 101,
|
||||
offset: 5,
|
||||
// zIndex: 101,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
|
||||
@@ -106,6 +106,7 @@ const MentionList = forwardRef<any, MentionListProps>((props, ref) => {
|
||||
|
||||
setRenderItems(items);
|
||||
// update editor storage
|
||||
//@ts-ignore
|
||||
props.editor.storage.mentionItems = items;
|
||||
}
|
||||
}, [suggestion, isLoading]);
|
||||
|
||||
@@ -73,6 +73,7 @@ const mentionRenderItems = () => {
|
||||
// destroy component if space is greater 3 without a match
|
||||
if (
|
||||
whitespaceCount > 3 &&
|
||||
//@ts-ignore
|
||||
props.editor.storage.mentionItems.length === 0
|
||||
) {
|
||||
popup?.[0]?.destroy();
|
||||
|
||||
+2
@@ -73,6 +73,8 @@ function SearchAndReplaceDialog({ editor, editable = true }: PageFindDialogDialo
|
||||
if (!editor) return;
|
||||
|
||||
const { results, resultIndex } = editor.storage.searchAndReplace;
|
||||
//TODO: check type error
|
||||
//@ts-ignore
|
||||
const position: Range = results[resultIndex];
|
||||
|
||||
if (!position) return;
|
||||
|
||||
@@ -159,6 +159,7 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
command: ({ editor, range }) => {
|
||||
editor.chain().focus().deleteRange(range).run();
|
||||
|
||||
// @ts-ignore
|
||||
const pageId = editor.storage?.pageId;
|
||||
if (!pageId) return;
|
||||
|
||||
@@ -186,6 +187,7 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
command: ({ editor, range }) => {
|
||||
editor.chain().focus().deleteRange(range).run();
|
||||
|
||||
// @ts-ignore
|
||||
const pageId = editor.storage?.pageId;
|
||||
if (!pageId) return;
|
||||
|
||||
@@ -211,6 +213,7 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
command: ({ editor, range }) => {
|
||||
editor.chain().focus().deleteRange(range).run();
|
||||
|
||||
// @ts-ignore
|
||||
const pageId = editor.storage?.pageId;
|
||||
if (!pageId) return;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react";
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import {
|
||||
@@ -57,19 +57,20 @@ export const TableCellMenu = React.memo(
|
||||
editor={editor}
|
||||
pluginKey="table-cell-menu"
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
appendTo: () => {
|
||||
return appendTo?.current;
|
||||
},
|
||||
offset: [0, 15],
|
||||
zIndex: 99,
|
||||
options={{
|
||||
//appendTo: () => {
|
||||
// return appendTo?.current;
|
||||
// },
|
||||
placement: "bottom",
|
||||
offset: 15,
|
||||
//zIndex: 99,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
<ActionIcon.Group>
|
||||
<TableBackgroundColor editor={editor} />
|
||||
<TableTextAlignment editor={editor} />
|
||||
|
||||
|
||||
<Tooltip position="top" label={t("Merge cells")}>
|
||||
<ActionIcon
|
||||
onClick={mergeCells}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
BubbleMenu as BaseBubbleMenu,
|
||||
posToDOMRect,
|
||||
findParentNode,
|
||||
} from "@tiptap/react";
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { posToDOMRect, findParentNode } from "@tiptap/react";
|
||||
import { Node as PMNode } from "@tiptap/pm/model";
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
@@ -17,9 +14,11 @@ import {
|
||||
IconColumnRemove,
|
||||
IconRowInsertBottom,
|
||||
IconRowInsertTop,
|
||||
IconRowRemove, IconTableColumn, IconTableRow,
|
||||
IconRowRemove,
|
||||
IconTableColumn,
|
||||
IconTableRow,
|
||||
IconTrashX,
|
||||
} from '@tabler/icons-react';
|
||||
} from "@tabler/icons-react";
|
||||
import { isCellSelection } from "@docmost/editor-ext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -91,38 +90,15 @@ export const TableMenu = React.memo(
|
||||
editor={editor}
|
||||
pluginKey="table-menu"
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
getReferenceClientRect: getReferenceClientRect,
|
||||
offset: [0, 15],
|
||||
zIndex: 99,
|
||||
popperOptions: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "preventOverflow",
|
||||
enabled: true,
|
||||
options: {
|
||||
altAxis: true,
|
||||
boundary: "clippingParents",
|
||||
padding: 8,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flip",
|
||||
enabled: true,
|
||||
options: {
|
||||
boundary: editor.options.element,
|
||||
fallbackPlacements: ["top", "bottom"],
|
||||
padding: { top: 35, left: 8, right: 8, bottom: -Infinity },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
options={{
|
||||
placement: "bottom",
|
||||
offset: 15,
|
||||
//zIndex: 99,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
<ActionIcon.Group>
|
||||
<Tooltip position="top" label={t("Add left column")}
|
||||
>
|
||||
<Tooltip position="top" label={t("Add left column")}>
|
||||
<ActionIcon
|
||||
onClick={addColumnLeft}
|
||||
variant="default"
|
||||
@@ -188,8 +164,7 @@ export const TableMenu = React.memo(
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip position="top" label={t("Toggle header row")}
|
||||
>
|
||||
<Tooltip position="top" label={t("Toggle header row")}>
|
||||
<ActionIcon
|
||||
onClick={toggleHeaderRow}
|
||||
variant="default"
|
||||
@@ -200,8 +175,7 @@ export const TableMenu = React.memo(
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip position="top" label={t("Toggle header column")}
|
||||
>
|
||||
<Tooltip position="top" label={t("Toggle header column")}>
|
||||
<ActionIcon
|
||||
onClick={toggleHeaderColumn}
|
||||
variant="default"
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import {
|
||||
BubbleMenu as BaseBubbleMenu,
|
||||
findParentNode,
|
||||
posToDOMRect,
|
||||
} from "@tiptap/react";
|
||||
import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus";
|
||||
import { findParentNode, posToDOMRect } from "@tiptap/react";
|
||||
import React, { useCallback } from "react";
|
||||
import { sticky } from "tippy.js";
|
||||
import { Node as PMNode } from "prosemirror-model";
|
||||
import {
|
||||
EditorMenuProps,
|
||||
@@ -85,15 +81,12 @@ export function VideoMenu({ editor }: EditorMenuProps) {
|
||||
editor={editor}
|
||||
pluginKey={`video-menu}`}
|
||||
updateDelay={0}
|
||||
tippyOptions={{
|
||||
getReferenceClientRect,
|
||||
offset: [0, 8],
|
||||
zIndex: 99,
|
||||
popperOptions: {
|
||||
modifiers: [{ name: "flip", enabled: false }],
|
||||
},
|
||||
plugins: [sticky],
|
||||
sticky: "popper",
|
||||
options={{
|
||||
//getReferenceClientRect,
|
||||
placement: "bottom",
|
||||
offset: 8,
|
||||
//zIndex: 99,
|
||||
flip: false,
|
||||
}}
|
||||
shouldShow={shouldShow}
|
||||
>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { StarterKit } from "@tiptap/starter-kit";
|
||||
import { Placeholder } from "@tiptap/extension-placeholder";
|
||||
import { TextAlign } from "@tiptap/extension-text-align";
|
||||
import { TaskList } from "@tiptap/extension-task-list";
|
||||
import { TaskItem } from "@tiptap/extension-task-item";
|
||||
import { Underline } from "@tiptap/extension-underline";
|
||||
import {
|
||||
TaskList,
|
||||
TaskItem,
|
||||
} from "@tiptap/extension-list";
|
||||
import {
|
||||
Placeholder,
|
||||
CharacterCount,
|
||||
} from "@tiptap/extensions";
|
||||
import { Superscript } from "@tiptap/extension-superscript";
|
||||
import SubScript from "@tiptap/extension-subscript";
|
||||
import { Highlight } from "@tiptap/extension-highlight";
|
||||
@@ -12,7 +16,7 @@ import { TextStyle } from "@tiptap/extension-text-style";
|
||||
import { Color } from "@tiptap/extension-color";
|
||||
import SlashCommand from "@/features/editor/extensions/slash-command";
|
||||
import { Collaboration } from "@tiptap/extension-collaboration";
|
||||
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
||||
import { CollaborationCaret } from "@tiptap/extension-collaboration-caret";
|
||||
import { HocuspocusProvider } from "@hocuspocus/provider";
|
||||
import {
|
||||
Comment,
|
||||
@@ -73,7 +77,6 @@ import MentionView from "@/features/editor/components/mention/mention-view.tsx";
|
||||
import i18n from "@/i18n.ts";
|
||||
import { MarkdownClipboard } from "@/features/editor/extensions/markdown-clipboard.ts";
|
||||
import EmojiCommand from "./emoji-command";
|
||||
import { CharacterCount } from "@tiptap/extension-character-count";
|
||||
import { countWords } from "alfaaz";
|
||||
|
||||
const lowlight = createLowlight(common);
|
||||
@@ -90,7 +93,7 @@ lowlight.register("scala", scala);
|
||||
|
||||
export const mainExtensions = [
|
||||
StarterKit.configure({
|
||||
history: false,
|
||||
undoRedo: false,
|
||||
dropcursor: {
|
||||
width: 3,
|
||||
color: "#70CFF8",
|
||||
@@ -101,6 +104,8 @@ export const mainExtensions = [
|
||||
spellcheck: false,
|
||||
},
|
||||
},
|
||||
link: false,
|
||||
trailingNode: false,
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: ({ node }) => {
|
||||
@@ -122,7 +127,6 @@ export const mainExtensions = [
|
||||
TaskItem.configure({
|
||||
nested: true,
|
||||
}),
|
||||
Underline,
|
||||
LinkExtension.configure({
|
||||
openOnClick: false,
|
||||
}),
|
||||
@@ -221,17 +225,17 @@ export const mainExtensions = [
|
||||
SearchAndReplace.extend({
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-f': () => {
|
||||
"Mod-f": () => {
|
||||
const event = new CustomEvent("openFindDialogFromEditor", {});
|
||||
document.dispatchEvent(event);
|
||||
return true;
|
||||
},
|
||||
'Escape': () => {
|
||||
Escape: () => {
|
||||
const event = new CustomEvent("closeFindDialogFromEditor", {});
|
||||
document.dispatchEvent(event);
|
||||
return true;
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
}).configure(),
|
||||
] as any;
|
||||
@@ -242,7 +246,7 @@ export const collabExtensions: CollabExtensions = (provider, user) => [
|
||||
Collaboration.configure({
|
||||
document: provider.document,
|
||||
}),
|
||||
CollaborationCursor.configure({
|
||||
CollaborationCaret.configure({
|
||||
provider,
|
||||
user: {
|
||||
name: user.name,
|
||||
|
||||
@@ -75,7 +75,7 @@ export default function PageEditor({
|
||||
const [isLocalSynced, setLocalSynced] = useState(false);
|
||||
const [isRemoteSynced, setRemoteSynced] = useState(false);
|
||||
const [yjsConnectionStatus, setYjsConnectionStatus] = useAtom(
|
||||
yjsConnectionStatusAtom
|
||||
yjsConnectionStatusAtom,
|
||||
);
|
||||
const menuContainerRef = useRef(null);
|
||||
const documentName = `page.${pageId}`;
|
||||
@@ -100,6 +100,7 @@ export default function PageEditor({
|
||||
|
||||
// Track when collaborative provider is ready and synced
|
||||
const [collabReady, setCollabReady] = useState(false);
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (
|
||||
remoteProvider?.status === WebSocketStatus.Connected &&
|
||||
@@ -109,6 +110,7 @@ export default function PageEditor({
|
||||
setCollabReady(true);
|
||||
}
|
||||
}, [remoteProvider?.status, isLocalSynced, isRemoteSynced]);
|
||||
*/
|
||||
|
||||
useEffect(() => {
|
||||
if (!providersRef.current) {
|
||||
@@ -119,8 +121,8 @@ export default function PageEditor({
|
||||
url: collaborationURL,
|
||||
document: ydoc,
|
||||
token: collabQuery?.token,
|
||||
connect: true,
|
||||
preserveConnection: false,
|
||||
//connect: true,
|
||||
//preserveConnection: false,
|
||||
onAuthenticationFailed: (auth: onAuthenticationFailedParameters) => {
|
||||
const payload = jwtDecode(collabQuery?.token);
|
||||
const now = Date.now().valueOf() / 1000;
|
||||
@@ -137,11 +139,11 @@ export default function PageEditor({
|
||||
});
|
||||
}
|
||||
},
|
||||
onStatus: (status) => {
|
||||
if (status.status === "connected") {
|
||||
setYjsConnectionStatus(status.status);
|
||||
}
|
||||
},
|
||||
//onStatus: (status) => {
|
||||
// if (status.status === "connected") {
|
||||
// setYjsConnectionStatus(status.status);
|
||||
// }
|
||||
// },
|
||||
});
|
||||
remote.on("synced", () => setRemoteSynced(true));
|
||||
remote.on("disconnect", () => {
|
||||
@@ -176,13 +178,14 @@ export default function PageEditor({
|
||||
*/
|
||||
|
||||
// Only connect/disconnect on tab/idle, not destroy
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (!providersReady || !providersRef.current) return;
|
||||
const remoteProvider = providersRef.current.remote;
|
||||
if (
|
||||
isIdle &&
|
||||
documentState === "hidden" &&
|
||||
remoteProvider.status === WebSocketStatus.Connected
|
||||
remoteProvider === WebSocketStatus.Connected
|
||||
) {
|
||||
remoteProvider.disconnect();
|
||||
setIsCollabReady(false);
|
||||
@@ -197,6 +200,7 @@ export default function PageEditor({
|
||||
setTimeout(() => setIsCollabReady(true), 500);
|
||||
}
|
||||
}, [isIdle, documentState, providersReady, resetIdle]);
|
||||
*/
|
||||
|
||||
const extensions = useMemo(() => {
|
||||
if (!remoteProvider || !currentUser?.user) return mainExtensions;
|
||||
@@ -217,7 +221,7 @@ export default function PageEditor({
|
||||
scrollMargin: 80,
|
||||
handleDOMEvents: {
|
||||
keydown: (_view, event) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.code === 'KeyS') {
|
||||
if ((event.ctrlKey || event.metaKey) && event.code === "KeyS") {
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
@@ -252,6 +256,7 @@ export default function PageEditor({
|
||||
if (editor) {
|
||||
// @ts-ignore
|
||||
setEditor(editor);
|
||||
// @ts-ignore
|
||||
editor.storage.pageId = pageId;
|
||||
}
|
||||
},
|
||||
@@ -262,7 +267,7 @@ export default function PageEditor({
|
||||
debouncedUpdateContent(editorJson);
|
||||
},
|
||||
},
|
||||
[pageId, editable, remoteProvider]
|
||||
[pageId, editable, remoteProvider],
|
||||
);
|
||||
|
||||
const debouncedUpdateContent = useDebouncedCallback((newContent: any) => {
|
||||
@@ -300,7 +305,7 @@ export default function PageEditor({
|
||||
return () => {
|
||||
document.removeEventListener(
|
||||
"ACTIVE_COMMENT_EVENT",
|
||||
handleActiveCommentEvent
|
||||
handleActiveCommentEvent,
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
@@ -311,6 +316,7 @@ export default function PageEditor({
|
||||
setAsideState({ tab: "", isAsideOpen: false });
|
||||
}, [pageId]);
|
||||
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (remoteProvider?.status === WebSocketStatus.Connecting) {
|
||||
const timeout = setTimeout(() => {
|
||||
@@ -319,9 +325,10 @@ export default function PageEditor({
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [remoteProvider?.status]);
|
||||
|
||||
*/
|
||||
const isSynced = isLocalSynced && isRemoteSynced;
|
||||
|
||||
/*
|
||||
useEffect(() => {
|
||||
const collabReadyTimeout = setTimeout(() => {
|
||||
if (
|
||||
@@ -334,6 +341,7 @@ export default function PageEditor({
|
||||
}, 500);
|
||||
return () => clearTimeout(collabReadyTimeout);
|
||||
}, [isRemoteSynced, isLocalSynced, remoteProvider?.status]);
|
||||
*/
|
||||
|
||||
useEffect(() => {
|
||||
// Only honor user default page edit mode preference and permissions
|
||||
@@ -351,8 +359,9 @@ export default function PageEditor({
|
||||
}, [userPageEditMode, editor, editable]);
|
||||
|
||||
const hasConnectedOnceRef = useRef(false);
|
||||
const [showStatic, setShowStatic] = useState(true);
|
||||
const [showStatic, setShowStatic] = useState(false);
|
||||
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (
|
||||
!hasConnectedOnceRef.current &&
|
||||
@@ -361,7 +370,7 @@ export default function PageEditor({
|
||||
hasConnectedOnceRef.current = true;
|
||||
setShowStatic(false);
|
||||
}
|
||||
}, [remoteProvider?.status]);
|
||||
}, [remoteProvider?.status]);*/
|
||||
|
||||
if (showStatic) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user