mirror of https://github.com/fantasticit/think.git
tiptap: fix isEditable
parent
578753c1f6
commit
b5172a8861
|
@ -92,21 +92,28 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
||||||
const [mentionUsers, setMentionUsers] = useState([]);
|
const [mentionUsers, setMentionUsers] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const indexdbProvider = getIndexdbProvider(documentId, provider.document);
|
|
||||||
|
|
||||||
indexdbProvider.on('synced', () => {
|
|
||||||
setStatus('loadCacheSuccess');
|
|
||||||
});
|
|
||||||
|
|
||||||
provider.on('status', async ({ status }) => {
|
provider.on('status', async ({ status }) => {
|
||||||
setStatus(status);
|
setStatus(status);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
destoryProvider(provider, 'EDITOR');
|
destoryProvider(provider, 'EDITOR');
|
||||||
|
};
|
||||||
|
}, [documentId, provider, authority]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!authority || !authority.editable) return;
|
||||||
|
|
||||||
|
const indexdbProvider = getIndexdbProvider(documentId, provider.document);
|
||||||
|
|
||||||
|
indexdbProvider.on('synced', () => {
|
||||||
|
setStatus('loadCacheSuccess');
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
destoryIndexdbProvider(documentId);
|
destoryIndexdbProvider(documentId);
|
||||||
};
|
};
|
||||||
}, [documentId, provider]);
|
}, [documentId, provider, authority]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useState, useEffect, DependencyList } from 'react';
|
||||||
|
import { EditorOptions } from '@tiptap/core';
|
||||||
|
import { Editor } from '@tiptap/react';
|
||||||
|
|
||||||
|
function useForceUpdate() {
|
||||||
|
const [, setValue] = useState(0);
|
||||||
|
|
||||||
|
return () => setValue((value) => value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useEditor = (options: Partial<EditorOptions> = {}, deps: DependencyList = []) => {
|
||||||
|
const [editor, setEditor] = useState<Editor | null>(null);
|
||||||
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const instance = new Editor(options);
|
||||||
|
|
||||||
|
setEditor(instance);
|
||||||
|
|
||||||
|
// instance.on('transaction', () => {
|
||||||
|
// requestAnimationFrame(() => {
|
||||||
|
// requestAnimationFrame(() => {
|
||||||
|
// console.log('update');
|
||||||
|
// forceUpdate();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
instance.destroy();
|
||||||
|
};
|
||||||
|
}, deps);
|
||||||
|
|
||||||
|
return editor;
|
||||||
|
};
|
|
@ -101,7 +101,8 @@ export const createDecorations = (state, awareness, createCursor) => {
|
||||||
export const yCursorPlugin = (
|
export const yCursorPlugin = (
|
||||||
awareness,
|
awareness,
|
||||||
{ cursorBuilder = defaultCursorBuilder, getSelection = (state) => state.selection } = {},
|
{ cursorBuilder = defaultCursorBuilder, getSelection = (state) => state.selection } = {},
|
||||||
cursorStateField = 'cursor'
|
cursorStateField = 'cursor',
|
||||||
|
isEditable = false
|
||||||
) =>
|
) =>
|
||||||
new Plugin({
|
new Plugin({
|
||||||
key: yCursorPluginKey,
|
key: yCursorPluginKey,
|
||||||
|
@ -131,6 +132,8 @@ export const yCursorPlugin = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const updateCursorInfo = () => {
|
const updateCursorInfo = () => {
|
||||||
|
if (!isEditable) return;
|
||||||
|
|
||||||
const ystate = ySyncPluginKey.getState(view.state);
|
const ystate = ySyncPluginKey.getState(view.state);
|
||||||
// @note We make implicit checks when checking for the cursor property
|
// @note We make implicit checks when checking for the cursor property
|
||||||
const current = awareness.getLocalState() || {};
|
const current = awareness.getLocalState() || {};
|
||||||
|
|
|
@ -165,6 +165,7 @@ export const CollaborationCursor = Extension.create<CollaborationCursorOptions,
|
||||||
|
|
||||||
addProseMirrorPlugins() {
|
addProseMirrorPlugins() {
|
||||||
const extensionThis = this;
|
const extensionThis = this;
|
||||||
|
const { isEditable } = this.editor;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
yCursorPlugin(
|
yCursorPlugin(
|
||||||
|
@ -184,7 +185,9 @@ export const CollaborationCursor = Extension.create<CollaborationCursorOptions,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
{
|
{
|
||||||
cursorBuilder: this.options.render,
|
cursorBuilder: this.options.render,
|
||||||
}
|
},
|
||||||
|
'cursor',
|
||||||
|
isEditable
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,6 +45,7 @@ export const SelectionExtension = Extension.create({
|
||||||
name: 'selection',
|
name: 'selection',
|
||||||
priority: EXTENSION_PRIORITY_HIGHEST,
|
priority: EXTENSION_PRIORITY_HIGHEST,
|
||||||
addProseMirrorPlugins() {
|
addProseMirrorPlugins() {
|
||||||
|
const { isEditable } = this.editor;
|
||||||
return [
|
return [
|
||||||
new Plugin({
|
new Plugin({
|
||||||
key: selectionPluginKey,
|
key: selectionPluginKey,
|
||||||
|
@ -103,6 +104,30 @@ export const SelectionExtension = Extension.create({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
new Plugin({
|
||||||
|
key: new PluginKey('preventSelection'),
|
||||||
|
props: {
|
||||||
|
// 禁止非可编辑用户选中
|
||||||
|
handleClick(_, __, event) {
|
||||||
|
if (!isEditable) {
|
||||||
|
event.preventDefault();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleKeyPress(_, event) {
|
||||||
|
if (!isEditable) {
|
||||||
|
event.preventDefault();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleKeyDown(_, event) {
|
||||||
|
if (!isEditable) {
|
||||||
|
event.preventDefault();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
import { Editor } from '@tiptap/core';
|
||||||
import { Space } from '@douyinfe/semi-ui';
|
import { Space } from '@douyinfe/semi-ui';
|
||||||
import { Divider } from './divider';
|
import { Divider } from './divider';
|
||||||
|
|
||||||
|
@ -43,11 +44,18 @@ import { Iframe } from './menus/iframe';
|
||||||
import { Table } from './menus/table';
|
import { Table } from './menus/table';
|
||||||
import { Mind } from './menus/mind';
|
import { Mind } from './menus/mind';
|
||||||
|
|
||||||
const _MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
const _MenuBar: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||||
|
const isEditable = useMemo(() => editor.isEditable, [editor]);
|
||||||
|
|
||||||
if (!editor) return null;
|
if (!editor) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
|
style={{
|
||||||
|
opacity: isEditable ? 1 : 0.65,
|
||||||
|
pointerEvents: isEditable ? 'auto' : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Space spacing={2}>
|
<Space spacing={2}>
|
||||||
<Insert editor={editor} />
|
<Insert editor={editor} />
|
||||||
|
|
||||||
|
@ -109,7 +117,7 @@ export const MenuBar = React.memo(_MenuBar, (prevProps, nextProps) => {
|
||||||
return prevProps.editor === nextProps.editor;
|
return prevProps.editor === nextProps.editor;
|
||||||
});
|
});
|
||||||
|
|
||||||
const _CommentMenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
const _CommentMenuBar: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Space spacing={2}>
|
<Space spacing={2}>
|
||||||
|
|
|
@ -20,7 +20,6 @@ export const useEditor = (options: Partial<EditorOptions> = {}, deps: Dependency
|
||||||
// instance.on('transaction', () => {
|
// instance.on('transaction', () => {
|
||||||
// requestAnimationFrame(() => {
|
// requestAnimationFrame(() => {
|
||||||
// requestAnimationFrame(() => {
|
// requestAnimationFrame(() => {
|
||||||
// console.log('update');
|
|
||||||
// forceUpdate();
|
// forceUpdate();
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
|
|
|
@ -133,7 +133,7 @@ export class BubbleMenuView {
|
||||||
trigger: 'manual',
|
trigger: 'manual',
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
hideOnClick: 'toggle',
|
hideOnClick: 'toggle',
|
||||||
...Object.assign({ zIndex: 99 }, this.tippyOptions),
|
...Object.assign({ zIndex: 999 }, this.tippyOptions),
|
||||||
});
|
});
|
||||||
|
|
||||||
// maybe we have to hide tippy on its own blur event as well
|
// maybe we have to hide tippy on its own blur event as well
|
||||||
|
@ -160,14 +160,16 @@ export class BubbleMenuView {
|
||||||
const from = Math.min(...ranges.map((range) => range.$from.pos));
|
const from = Math.min(...ranges.map((range) => range.$from.pos));
|
||||||
const to = Math.max(...ranges.map((range) => range.$to.pos));
|
const to = Math.max(...ranges.map((range) => range.$to.pos));
|
||||||
|
|
||||||
const shouldShow = this.shouldShow?.({
|
const shouldShow =
|
||||||
editor: this.editor,
|
this.editor.isEditable &&
|
||||||
view,
|
this.shouldShow?.({
|
||||||
state,
|
editor: this.editor,
|
||||||
oldState,
|
view,
|
||||||
from,
|
state,
|
||||||
to,
|
oldState,
|
||||||
});
|
from,
|
||||||
|
to,
|
||||||
|
});
|
||||||
|
|
||||||
if (!shouldShow) {
|
if (!shouldShow) {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
Loading…
Reference in New Issue