diff --git a/libs/components/editor-core/src/RenderRoot.tsx b/libs/components/editor-core/src/RenderRoot.tsx index 13c2bbbc82..7ab57cfa60 100644 --- a/libs/components/editor-core/src/RenderRoot.tsx +++ b/libs/components/editor-core/src/RenderRoot.tsx @@ -11,7 +11,7 @@ import { type ReturnUnobserve, } from '@toeverything/datasource/db-service'; import { addNewGroup } from './recast-block'; -import { HookType } from './editor'; +import { useIsOnDrag } from '.'; interface RenderRootProps { editor: BlockEditor; @@ -35,6 +35,7 @@ export const RenderRoot: FC> = ({ const triggeredBySelect = useRef(false); const [container, setContainer] = useState(); const [pageWidth, setPageWidth] = useState(MIN_PAGE_WIDTH); + const isOnDrag = useIsOnDrag(editor); const { patch, has, patchedNodes } = usePatchNodes(); @@ -147,6 +148,8 @@ export const RenderRoot: FC> = ({ }; const onDragOver = (event: React.DragEvent) => { + event.dataTransfer.dropEffect = 'move'; + event.preventDefault(); if (!contentRef.current) { return; } @@ -155,10 +158,10 @@ export const RenderRoot: FC> = ({ if (editor.dragDropManager.isEnabled()) { editor.getHooks().onRootNodeDragOver(event, rootRect); } - event.preventDefault(); }; const onDragOverCapture = (event: React.DragEvent) => { + event.preventDefault(); if (!contentRef.current) { return; } @@ -202,7 +205,7 @@ export const RenderRoot: FC> = ({ onDragOverCapture={onDragOverCapture} onDragEnd={onDragEnd} onDrop={onDrop} - onScroll={onScroll} + isOnDrag={isOnDrag} > ({ + ({ + isWhiteboard, + isOnDrag, + }: { + isWhiteboard: boolean; + isOnDrag: boolean; + }) => ({ width: '100%', height: '100%', overflowY: isWhiteboard ? 'unset' : 'auto', padding: isWhiteboard ? 0 : '96px 150px 0 150px', minWidth: isWhiteboard ? 'unset' : '940px', position: 'relative', + ...(isOnDrag && { + cursor: 'grabbing', + // expected css selector + // eslint-disable-next-line @typescript-eslint/naming-convention + '& *': { cursor: 'grabbing' }, + }), }) ); diff --git a/libs/components/editor-core/src/drag-drop-wrapper/DragDropWrapper.tsx b/libs/components/editor-core/src/drag-drop-wrapper/DragDropWrapper.tsx index f2d7a2afc2..6ef1db4c92 100644 --- a/libs/components/editor-core/src/drag-drop-wrapper/DragDropWrapper.tsx +++ b/libs/components/editor-core/src/drag-drop-wrapper/DragDropWrapper.tsx @@ -17,8 +17,8 @@ export function DragDropWrapper({ const handler_drag_over: React.DragEventHandler< HTMLDivElement > = async event => { + event.preventDefault(); const rootDom = await editor.getBlockDomById(editor.getRootBlockId()); - if (block.dom && rootDom) { editor.getHooks().afterOnNodeDragOver(event, { blockId: block.id, diff --git a/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts b/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts index 2da6d1a425..e3da55fcb5 100644 --- a/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts +++ b/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts @@ -9,9 +9,12 @@ import EventEmitter from 'eventemitter3'; type DargType = | ValueOf['dragActions']> | ''; + +const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange'; export class DragDropManager { private _editor: Editor; private _enabled: boolean; + private _events = new EventEmitter(); private _blockIdKey = 'blockId'; private _rootIdKey = 'rootId'; @@ -26,15 +29,29 @@ export class DragDropManager { dragGroup: 'dragGroup', } as const; + private _isOnDrag = false; + get dragActions() { return this._dragActions; } + get isOnDrag() { + return this._isOnDrag; + } + + set isOnDrag(isOnDrag: boolean) { + if (this._isOnDrag !== isOnDrag) { + this._events.emit(DRAG_STATE_CHANGE_EVENT_KEY, isOnDrag); + } + this._isOnDrag = isOnDrag; + } + constructor(editor: Editor) { this._editor = editor; this._enabled = true; this._dragType = ''; this._blockDragDirection = BlockDropPlacement.none; + this._initMouseEvent(); } get dragType() { @@ -45,6 +62,15 @@ export class DragDropManager { this._dragType = type; } + private async _initMouseEvent() { + this._editor.mouseManager.onMouseUp(() => { + // IMP: temporarily last protect for dragging across editor instances,bad case + if (this.isOnDrag) { + this.isOnDrag = false; + } + }); + } + private _setBlockDragDirection(direction: BlockDropPlacement) { this._blockDragDirection = direction; } @@ -244,6 +270,9 @@ export class DragDropManager { public handlerEditorDragEnd(event: React.DragEvent) { this._resetDragDropData(); + if (this.isOnDrag) { + this.isOnDrag = false; + } if (this.isEnabled()) { // TODO: handle drag end event flow } @@ -287,4 +316,16 @@ export class DragDropManager { } return direction; } + + public onDragStateChange(cb: (isOnDrag: boolean) => void) { + this._events.on(DRAG_STATE_CHANGE_EVENT_KEY, cb); + } + + public offDragStateChange(cb: (isOnDrag: boolean) => void) { + this._events.off(DRAG_STATE_CHANGE_EVENT_KEY, cb); + } + + public dispose() { + this._events.removeAllListeners(); + } } diff --git a/libs/components/editor-core/src/editor/types.ts b/libs/components/editor-core/src/editor/types.ts index 0f6b09afb3..a4166ecfdf 100644 --- a/libs/components/editor-core/src/editor/types.ts +++ b/libs/components/editor-core/src/editor/types.ts @@ -20,6 +20,7 @@ import type { AsyncBlock } from './block'; import type { BlockHelper } from './block/block-helper'; import type { BlockCommands } from './commands/block-commands'; import type { DragDropManager } from './drag-drop'; +import { MouseManager } from './mouse'; // import { BrowserClipboard } from './clipboard/browser-clipboard'; @@ -104,6 +105,7 @@ export interface Virgo { workspace: string; getBlockDomById: (id: string) => Promise; isWhiteboard: boolean; + mouseManager: MouseManager; } export interface Plugin { diff --git a/libs/components/editor-core/src/hooks.ts b/libs/components/editor-core/src/hooks.ts index 82fd137730..d7d1a00cef 100644 --- a/libs/components/editor-core/src/hooks.ts +++ b/libs/components/editor-core/src/hooks.ts @@ -1,5 +1,6 @@ import { AsyncBlock, + BlockEditor, SelectEventTypes, SelectionInfo, SelectionSettingsMap, @@ -202,3 +203,21 @@ export const useOnSelectStartWith = ( }; }, [editor, cb, blockId]); }; + +/** + * + * hooks run when drag state change + * @export + */ +export const useIsOnDrag = (editor: BlockEditor) => { + const editorInstance = editor; + const [isOnDrag, setIsOnDrag] = useState(false); + useEffect(() => { + const callback = (isDrag: boolean) => setIsOnDrag(isDrag); + editorInstance.dragDropManager.onDragStateChange(callback); + return () => { + editorInstance.dragDropManager.offDragStateChange(callback); + }; + }, [editorInstance]); + return isOnDrag; +}; diff --git a/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx b/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx index 642cbeeac2..a323ca1961 100644 --- a/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx +++ b/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx @@ -10,6 +10,7 @@ type DragItemProps = { isShow: boolean; groupBlock: AsyncBlock; editor: Virgo; + item: React.MutableRefObject; onPositionChange?: (position: Point) => void; } & React.HTMLAttributes; @@ -17,10 +18,11 @@ export const DragItem = function ({ isShow, editor, groupBlock, + item, ...divProps }: DragItemProps) { return ( - + @@ -33,13 +35,14 @@ const StyledDiv = styled('div')({ display: 'inlineFlex', width: `${ICON_WIDTH}px`, height: `${ICON_WIDTH}px`, + cursor: 'grab', ':hover': { backgroundColor: '#edeef0', borderRadius: '4px', }, }); -const StyledButton = styled(Button)({ +const StyledButton = styled('div')({ padding: '0', display: 'inlineFlex', alignItems: 'center', diff --git a/libs/components/editor-plugins/src/menu/group-menu/GropuMenu.tsx b/libs/components/editor-plugins/src/menu/group-menu/GropuMenu.tsx index 9b97b33c69..0c6bc70485 100644 --- a/libs/components/editor-plugins/src/menu/group-menu/GropuMenu.tsx +++ b/libs/components/editor-plugins/src/menu/group-menu/GropuMenu.tsx @@ -23,6 +23,7 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) { const [direction, setDirection] = useState( GroupDirection.down ); + const dragItemRef = useRef(null); const menuRef = useRef(null); const handleRootMouseMove = useCallback( @@ -50,6 +51,7 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) { const handleRootDragOver = useCallback( async (e: React.DragEvent) => { + e.preventDefault(); let groupBlockOnDragOver = null; const mousePoint = new Point(e.clientX, e.clientY); if (editor.dragDropManager.isDragGroup(e)) { @@ -89,7 +91,7 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) { [editor, groupBlock] ); - const handleRootDragEnd = () => { + const handleRootDragEnd = (e: DragEvent) => { setDragOverGroup(null); }; @@ -139,13 +141,16 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) { }; const handleDragStart = async (e: React.DragEvent) => { + editor.dragDropManager.isOnDrag = true; const dragImage = await editor.blockHelper.getBlockDragImg( groupBlock.id ); if (dragImage) { - e.dataTransfer.setDragImage(dragImage, 0, 0); editor.dragDropManager.setDragGroupInfo(e, groupBlock.id); + dragImage.style.cursor = 'grabbing !important'; + e.dataTransfer.setDragImage(dragImage, 0, 0); } + e.dataTransfer.setData('text/plain', groupBlock.id); }; const handleMouseDown = (e: React.MouseEvent) => { @@ -169,11 +174,15 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) { menuRef={menuRef} > { + editor.dragDropManager.isOnDrag = true; + }} onMouseDown={handleMouseDown} draggable={true} /> diff --git a/libs/components/editor-plugins/src/menu/group-menu/Plugin.tsx b/libs/components/editor-plugins/src/menu/group-menu/Plugin.tsx index 93cab7babb..8774d7fca6 100644 --- a/libs/components/editor-plugins/src/menu/group-menu/Plugin.tsx +++ b/libs/components/editor-plugins/src/menu/group-menu/Plugin.tsx @@ -15,9 +15,6 @@ export class GroupMenuPlugin extends BasePlugin { protected override on_render(): void { if (this.editor.isWhiteboard) return; - const container = document.createElement('div'); - // TODO remove - container.classList.add(`id-${PLUGIN_NAME}`); this.root = new PluginRenderRoot({ name: PLUGIN_NAME, render: this.editor.reactRenderRoot.render, diff --git a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx index 7a7706129a..af40c73014 100644 --- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx +++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx @@ -133,6 +133,7 @@ export const LeftMenuDraggable: FC = props => { ); const onDragStart = async (event: React.DragEvent) => { + editor.dragDropManager.isOnDrag = true; if (block == null) return; const dragImage = await editor.blockHelper.getBlockDragImg( block.blockId diff --git a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx index 65323f6d1b..d0e21c8063 100644 --- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx +++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx @@ -55,8 +55,8 @@ export class LeftMenuPlugin extends BasePlugin { blockInfo: BlockDomInfo ) => { const { type, dom, blockId } = blockInfo; + event.preventDefault(); if (this.editor.dragDropManager.isDragBlock(event)) { - event.preventDefault(); if (ignoreBlockTypes.includes(type)) { return; }