tiptap: improve drag

pull/123/head
fantasticit 2022-07-14 21:49:39 +08:00
parent 9560066cfa
commit 5f773ec988
14 changed files with 67 additions and 19 deletions

View File

@ -28,6 +28,7 @@ export const Dragable = Extension.create({
name: 'dragable',
addProseMirrorPlugins() {
let scrollContainer;
let dropElement;
let currentNode;
let editorView;
@ -54,6 +55,11 @@ export const Dragable = Extension.create({
editorView.dragging = { slice, move: true };
}
function onScroll() {
if (!dropElement) return;
dropElement.style.opacity = 0;
}
return [
new Plugin({
view(view) {
@ -64,6 +70,12 @@ export const Dragable = Extension.create({
dropElement.className = 'drag-handler';
dropElement.addEventListener('dragstart', drag);
view.dom.parentElement.appendChild(dropElement);
scrollContainer = view.dom.parentElement.parentElement?.parentElement?.parentElement;
if (scrollContainer) {
scrollContainer.addEventListener('scroll', onScroll);
}
}
return {
@ -75,6 +87,10 @@ export const Dragable = Extension.create({
dropElement.removeEventListener('dragstart', drag);
dropElement.parentNode.removeChild(dropElement);
}
if (scrollContainer) {
scrollContainer.removeEventListener('scroll', onScroll);
}
},
};
},
@ -91,7 +107,7 @@ export const Dragable = Extension.create({
}
}, 50);
},
mousemove(view, event) {
mousedown(view, event) {
if (!dropElement) return;
const coords = { left: event.clientX, top: event.clientY };
@ -134,10 +150,6 @@ export const Dragable = Extension.create({
dropElement.style.top = rect.top + 6 + 'px';
dropElement.style.opacity = 1;
},
mouseleave() {
if (!dropElement || currentNode) return;
dropElement.style.opacity = 0;
},
},
},
}),

View File

@ -6,6 +6,7 @@ import { Tooltip } from 'components/tooltip';
import { useToggle } from 'hooks/use-toggle';
import { useCallback, useEffect, useRef, useState } from 'react';
import { uploadFile } from 'services/file';
import { Attachment } from 'tiptap/core/extensions/attachment';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import { download, extractFileExtension, extractFilename, normalizeFileSize } from 'tiptap/prose-utils';
@ -154,5 +155,9 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => {
}
})();
return <DragableWrapper editor={editor}>{content}</DragableWrapper>;
return (
<DragableWrapper editor={editor} extensionName={Attachment.name}>
{content}
</DragableWrapper>
);
};

View File

@ -4,6 +4,7 @@ import { EmojiPicker } from 'components/emoji-picker';
import { convertColorToRGBA } from 'helpers/color';
import { Theme, ThemeEnum } from 'hooks/use-theme';
import { useCallback, useMemo } from 'react';
import { Callout } from 'tiptap/core/extensions/callout';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import styles from './index.module.scss';
@ -26,7 +27,12 @@ export const CalloutWrapper = ({ editor, node, updateAttributes }) => {
);
return (
<DragableWrapper editor={editor} id="js-callout-container" className={cls(styles.wrap)}>
<DragableWrapper
editor={editor}
extensionName={Callout.name}
id="js-callout-container"
className={cls(styles.wrap)}
>
<div
className={cls(styles.innerWrap, 'render-wrapper')}
style={{

View File

@ -4,6 +4,7 @@ import { NodeViewContent } from '@tiptap/react';
import cls from 'classnames';
import { copy } from 'helpers/copy';
import React, { useRef } from 'react';
import { CodeBlock } from 'tiptap/core/extensions/code-block';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import styles from './index.module.scss';
@ -14,7 +15,7 @@ export const CodeBlockWrapper = ({ editor, node: { attrs }, updateAttributes, ex
const $container = useRef<HTMLPreElement>();
return (
<DragableWrapper editor={editor} className={cls(styles.wrap, 'render-wrapper')}>
<DragableWrapper editor={editor} extensionName={CodeBlock.name} className={cls(styles.wrap, 'render-wrapper')}>
<div className={styles.handleWrap}>
<Select
size="small"

View File

@ -1,6 +1,7 @@
import { Space, Typography } from '@douyinfe/semi-ui';
import cls from 'classnames';
import Countdown from 'react-countdown';
import ReactCountdown from 'react-countdown';
import { Countdown } from 'tiptap/core/extensions/countdown';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import styles from './index.module.scss';
@ -32,10 +33,10 @@ export const CountdownWrapper = ({ editor, node }) => {
const { title, date } = node.attrs;
return (
<DragableWrapper editor={editor}>
<DragableWrapper editor={editor} extensionName={Countdown.name}>
<div className={cls(styles.wrap, 'render-wrapper')}>
<Text>{title}</Text>
<Countdown date={date} renderer={renderer}></Countdown>
<ReactCountdown date={date} renderer={renderer}></ReactCountdown>
</div>
</DragableWrapper>
);

View File

@ -7,6 +7,7 @@ import { useChildrenDocument } from 'data/document';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { DocumentChildren } from 'tiptap/core/extensions/document-children';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import styles from './index.module.scss';
@ -37,6 +38,7 @@ export const DocumentChildrenWrapper = ({ editor, node, updateAttributes }) => {
return (
<DragableWrapper
editor={editor}
extensionName={DocumentChildren.name}
as="div"
className={cls('render-wrapper', styles.wrap, isEditable && styles.isEditable, 'documentChildren')}
>

View File

@ -3,6 +3,7 @@ import { IconDocument } from 'components/icons';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useMemo } from 'react';
import { DocumentReference } from 'tiptap/core/extensions/document-reference';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import styles from './index.module.scss';
@ -50,7 +51,12 @@ export const DocumentReferenceWrapper = ({ editor, node, updateAttributes }) =>
}, [organizationId, wikiId, documentId, isEditable, isShare, title]);
return (
<DragableWrapper editor={editor} as="div" className={cls(styles.wrap, isEditable && 'render-wrapper')}>
<DragableWrapper
editor={editor}
extensionName={DocumentReference.name}
as="div"
className={cls(styles.wrap, isEditable && 'render-wrapper')}
>
{content}
</DragableWrapper>
);

View File

@ -21,7 +21,7 @@
}
&.isEditable {
&:hover {
&.isActive {
.dragHandle {
opacity: 1;
}

View File

@ -2,23 +2,26 @@ import { Editor } from '@tiptap/core';
import { NodeViewWrapper } from '@tiptap/react';
import cls from 'classnames';
import React, { ElementType } from 'react';
import { useActive } from 'tiptap/core/hooks/use-active';
import styles from './index.module.scss';
export const DragableWrapper: React.FC<{
editor: Editor;
extensionName: string;
as?: ElementType;
id?: string;
className?: string;
style?: React.CSSProperties;
}> = ({ editor, as = 'div', id, className, style = {}, children }) => {
}> = ({ editor, extensionName, as = 'div', id, className, style = {}, children }) => {
const isEditable = editor.isEditable;
const isActive = useActive(editor, extensionName);
return (
<NodeViewWrapper
as={as}
id={id}
className={cls(styles.draggableItem, isEditable && styles.isEditable, className)}
className={cls(styles.draggableItem, isEditable && styles.isEditable, isActive && styles.isActive, className)}
style={style}
>
<div className={styles.dragHandle} contentEditable="false" draggable="true" data-drag-handle />

View File

@ -95,7 +95,11 @@ export const FlowWrapper = ({ editor, node, updateAttributes }) => {
}, [toggleLoading, data]);
return (
<DragableWrapper editor={editor} className={cls(styles.wrap, isActive && styles.isActive)}>
<DragableWrapper
editor={editor}
className={cls(styles.wrap, isActive && styles.isActive)}
extensionName={Flow.name}
>
<VisibilitySensor onChange={onViewportChange}>
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
<div

View File

@ -2,6 +2,7 @@ import { Typography } from '@douyinfe/semi-ui';
import cls from 'classnames';
import { Resizeable } from 'components/resizeable';
import { useCallback } from 'react';
import { Iframe } from 'tiptap/core/extensions/iframe';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
@ -22,7 +23,7 @@ export const IframeWrapper = ({ editor, node, updateAttributes }) => {
);
return (
<DragableWrapper editor={editor}>
<DragableWrapper editor={editor} extensionName={Iframe.name}>
<Resizeable width={width} maxWidth={maxWidth} height={height} isEditable={isEditable} onChangeEnd={onResize}>
<div className={cls(styles.wrap, 'render-wrapper')}>
{url ? (

View File

@ -4,6 +4,7 @@ import { useToggle } from 'hooks/use-toggle';
import { useCallback, useEffect, useRef } from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import { uploadFile } from 'services/file';
import { Image } from 'tiptap/core/extensions/image';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import {
extractFileExtension,
@ -69,7 +70,7 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => {
}, [src, hasTrigger, selectFile, updateAttributes]);
return (
<DragableWrapper editor={editor} style={{ textAlign, fontSize: 0, maxWidth: '100%' }}>
<DragableWrapper editor={editor} extensionName={Image.name} style={{ textAlign, fontSize: 0, maxWidth: '100%' }}>
<Resizeable
className={'render-wrapper'}
width={width || maxWidth}

View File

@ -2,6 +2,7 @@ import { convertColorToRGBA } from 'helpers/color';
import { Theme, ThemeEnum } from 'hooks/use-theme';
import katex from 'katex';
import { useMemo } from 'react';
import { Katex } from 'tiptap/core/extensions/katex';
import { DragableWrapper } from 'tiptap/core/wrappers/dragable';
import styles from './index.module.scss';
@ -36,6 +37,7 @@ export const KatexWrapper = ({ node, editor }) => {
return (
<DragableWrapper
editor={editor}
extensionName={Katex.name}
className={'render-wrapper'}
style={{
backgroundColor,

View File

@ -108,7 +108,11 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
}, [width, height, setCenter]);
return (
<DragableWrapper editor={editor} className={cls(styles.wrap, isActive && styles.isActive)}>
<DragableWrapper
editor={editor}
extensionName={Mind.name}
className={cls(styles.wrap, isActive && styles.isActive)}
>
<VisibilitySensor onChange={onViewportChange}>
<Resizeable isEditable={isEditable} width={width} height={height} maxWidth={maxWidth} onChangeEnd={onResize}>
<div