diff --git a/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx b/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx index eb6c334803..2513ce3c08 100644 --- a/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx +++ b/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx @@ -124,7 +124,12 @@ const EditorContainer = ({ return ( { + scrollContainerRef.current = ref; + if (editorRef.current?.scrollManager) { + editorRef.current.scrollManager.scrollContainer = ref; + } + }} onScroll={onScroll} > {pageId ? ( diff --git a/libs/components/editor-blocks/src/blocks/grid-item/GridItem.tsx b/libs/components/editor-blocks/src/blocks/grid-item/GridItem.tsx index 491ac66628..4273e7c5d1 100644 --- a/libs/components/editor-blocks/src/blocks/grid-item/GridItem.tsx +++ b/libs/components/editor-blocks/src/blocks/grid-item/GridItem.tsx @@ -2,13 +2,13 @@ import { FC, useEffect, useLayoutEffect, useRef } from 'react'; import { ChildrenView } from '@toeverything/framework/virgo'; import { styled } from '@toeverything/components/ui'; import { sleep } from '@toeverything/utils'; -import { GRID_ITEM_MIN_WIDTH, GRID_PROPERTY_KEY, removePercent } from '../grid'; +import { GRID_PROPERTY_KEY, removePercent } from '../grid'; export const GRID_ITEM_CLASS_NAME = 'grid-item'; export const GRID_ITEM_CONTENT_CLASS_NAME = `${GRID_ITEM_CLASS_NAME}-content`; export const GridItem: FC = function (props) { - const { children, block } = props; + const { children, block, editor } = props; const RENDER_DELAY_TIME = 100; const ref = useRef(); @@ -25,6 +25,7 @@ export const GridItem: FC = function (props) { const checkAndRefreshWidth = async () => { const currentWidth = block.getProperty(GRID_PROPERTY_KEY); + const gridItemMinWidth = editor.configManager.grid.gridItemMinWidth; if (currentWidth) { setWidth(currentWidth); } else if (!block.dom?.style.width) { @@ -64,26 +65,23 @@ export const GridItem: FC = function (props) { if new width less then min width, set min width and next block will be fix width */ - if (newWidth < GRID_ITEM_MIN_WIDTH) { - needFixWidth += GRID_ITEM_MIN_WIDTH - newWidth; - newWidth = GRID_ITEM_MIN_WIDTH; + if (newWidth < gridItemMinWidth) { + needFixWidth += gridItemMinWidth - newWidth; + newWidth = gridItemMinWidth; } // if can fix width, fix width - if ( - newWidth > GRID_ITEM_MIN_WIDTH && - needFixWidth - ) { + if (newWidth > gridItemMinWidth && needFixWidth) { if ( newWidth - needFixWidth >= - GRID_ITEM_MIN_WIDTH + gridItemMinWidth ) { newWidth = newWidth - needFixWidth; needFixWidth = 0; } else { needFixWidth = needFixWidth - - (newWidth - GRID_ITEM_MIN_WIDTH); - newWidth = GRID_ITEM_MIN_WIDTH; + (newWidth - gridItemMinWidth); + newWidth = gridItemMinWidth; } } if (index === children.length - 2) { diff --git a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx index bc27ec4ac6..ec06c1049f 100644 --- a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx +++ b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx @@ -12,10 +12,8 @@ import { debounce, domToRect, Point } from '@toeverything/utils'; import clsx from 'clsx'; import { Protocol } from '@toeverything/datasource/db-service'; -const MAX_ITEM_COUNT = 6; const DB_UPDATE_DELAY = 50; const GRID_ON_DRAG_CLASS = 'grid-layout-on-drag'; -export const GRID_ITEM_MIN_WIDTH = 100 / MAX_ITEM_COUNT; export const GRID_PROPERTY_KEY = 'gridItemWidth'; export function removePercent(str: string) { @@ -24,13 +22,14 @@ export function removePercent(str: string) { export const Grid: FC = function (props) { const { block, editor } = props; + const gridItemMinWidth = editor.configManager.grid.gridItemMinWidth; const [isOnDrag, setIsOnDrag] = useState(false); const isSetMouseUp = useRef(false); const gridContainerRef = useRef(); const mouseStartPoint = useRef(); const gridItemCountRef = useRef(); - const originalLeftWidth = useRef(GRID_ITEM_MIN_WIDTH); - const originalRightWidth = useRef(GRID_ITEM_MIN_WIDTH); + const originalLeftWidth = useRef(gridItemMinWidth); + const originalRightWidth = useRef(gridItemMinWidth); const [alertHandleId, setAlertHandleId] = useState(null); const getLeftRightGridItemDomByIndex = (index: number) => { @@ -126,8 +125,8 @@ export const Grid: FC = function (props) { editor.mouseManager.onMouseupEventOnce(() => { setIsOnDrag(false); isSetMouseUp.current = false; - originalLeftWidth.current = GRID_ITEM_MIN_WIDTH; - originalRightWidth.current = GRID_ITEM_MIN_WIDTH; + originalLeftWidth.current = gridItemMinWidth; + originalRightWidth.current = gridItemMinWidth; mouseStartPoint.current = null; }); } else { @@ -153,12 +152,12 @@ export const Grid: FC = function (props) { const newLeftWidth = originalLeftWidth.current - xDistance; let newLeftPercent = (newLeftWidth / containerWidth) * 100; let newRightPercent = Number(totalWidth) - newLeftPercent; - if (newLeftPercent < GRID_ITEM_MIN_WIDTH) { - newLeftPercent = GRID_ITEM_MIN_WIDTH; - newRightPercent = totalWidth - GRID_ITEM_MIN_WIDTH; - } else if (newRightPercent < GRID_ITEM_MIN_WIDTH) { - newRightPercent = GRID_ITEM_MIN_WIDTH; - newLeftPercent = totalWidth - GRID_ITEM_MIN_WIDTH; + if (newLeftPercent < gridItemMinWidth) { + newLeftPercent = gridItemMinWidth; + newRightPercent = totalWidth - gridItemMinWidth; + } else if (newRightPercent < gridItemMinWidth) { + newRightPercent = gridItemMinWidth; + newLeftPercent = totalWidth - gridItemMinWidth; } //XXX first change dom style is for animation speed, maybe not a good idea const newLeft = `${newLeftPercent}%`; @@ -213,6 +212,7 @@ export const Grid: FC = function (props) { {block.childrenIds.map((id, i) => { @@ -233,7 +233,8 @@ export const Grid: FC = function (props) { onMouseDown={event => handleMouseDown(event, i)} blockId={id} enabledAddItem={ - block.childrenIds.length < MAX_ITEM_COUNT + block.childrenIds.length < + editor.configManager.grid.maxGridItemCount } onMouseEnter={event => handleHandleMouseEnter(event, i) @@ -252,24 +253,25 @@ export const Grid: FC = function (props) { ); }; -const GridContainer = styled('div')<{ isOnDrag: boolean }>( - ({ isOnDrag, theme }) => ({ - position: 'relative', - display: 'flex', - alignItems: 'stretch', - borderRadius: '10px', - border: '1px solid #FFF', - minWidth: `${GRID_ITEM_MIN_WIDTH}%`, - [`&:hover .${GRID_ITEM_CONTENT_CLASS_NAME}`]: { +const GridContainer = styled('div')<{ + isOnDrag: boolean; + gridItemMinWidth: number; +}>(({ isOnDrag, theme, gridItemMinWidth }) => ({ + position: 'relative', + display: 'flex', + alignItems: 'stretch', + borderRadius: '10px', + border: '1px solid #FFF', + minWidth: `${gridItemMinWidth}%`, + [`&:hover .${GRID_ITEM_CONTENT_CLASS_NAME}`]: { + borderColor: theme.affine.palette.borderColor, + }, + ...(isOnDrag && { + [`& .${GRID_ITEM_CONTENT_CLASS_NAME}`]: { borderColor: theme.affine.palette.borderColor, }, - ...(isOnDrag && { - [`& .${GRID_ITEM_CONTENT_CLASS_NAME}`]: { - borderColor: theme.affine.palette.borderColor, - }, - }), - }) -); + }), +})); const GridMask = styled('div')({ position: 'fixed', diff --git a/libs/components/editor-blocks/src/blocks/grid/index.ts b/libs/components/editor-blocks/src/blocks/grid/index.ts index ec6c363819..808f54f0aa 100644 --- a/libs/components/editor-blocks/src/blocks/grid/index.ts +++ b/libs/components/editor-blocks/src/blocks/grid/index.ts @@ -3,7 +3,7 @@ import { Protocol } from '@toeverything/datasource/db-service'; import { AsyncBlock, BaseView } from '@toeverything/framework/virgo'; import { GridItem } from '../grid-item/GridItem'; import { GridRender } from './GridRender'; -export { GRID_ITEM_MIN_WIDTH, GRID_PROPERTY_KEY, removePercent } from './Grid'; +export { GRID_PROPERTY_KEY, removePercent } from './Grid'; export class GridBlock extends BaseView { public override selectable = false; diff --git a/libs/components/editor-core/src/Selection.tsx b/libs/components/editor-core/src/Selection.tsx index 4a31aa17d2..e2dc34ed3e 100644 --- a/libs/components/editor-core/src/Selection.tsx +++ b/libs/components/editor-core/src/Selection.tsx @@ -187,7 +187,6 @@ export const SelectionRect = forwardRef( ) ) ); - const scrollDirections = getScrollDirections( endPointRef.current, scrollManager.verticalScrollTriggerDistance, @@ -204,6 +203,7 @@ export const SelectionRect = forwardRef( mouseType.current = 'up'; startPointBlock.current = null; setShow(false); + setRect(Rect.fromLTRB(0, 0, 0, 0)); scrollManager.stopAutoScroll(); }; diff --git a/libs/components/editor-core/src/editor/commands/block-commands.ts b/libs/components/editor-core/src/editor/commands/block-commands.ts index f21a36d56d..54f9e5d2fe 100644 --- a/libs/components/editor-core/src/editor/commands/block-commands.ts +++ b/libs/components/editor-core/src/editor/commands/block-commands.ts @@ -159,6 +159,33 @@ export class BlockCommands { return []; } + public async moveInNewGridItem( + blockId: string, + gridItemId: string, + isBefore = false + ) { + const block = await this._editor.getBlockById(blockId); + if (block) { + const gridItemBlock = await this._editor.createBlock( + Protocol.Block.Type.gridItem + ); + const targetGridItemBlock = await this._editor.getBlockById( + gridItemId + ); + await block.remove(); + await gridItemBlock.append(block); + if (targetGridItemBlock && gridItemBlock) { + if (isBefore) { + await targetGridItemBlock.before(gridItemBlock); + } else { + await targetGridItemBlock.after(gridItemBlock); + } + } + return gridItemBlock; + } + return undefined; + } + public async splitGroupFromBlock(blockId: string) { const block = await this._editor.getBlockById(blockId); await splitGroup(this._editor, block); diff --git a/libs/components/editor-core/src/editor/config/grid.ts b/libs/components/editor-core/src/editor/config/grid.ts new file mode 100644 index 0000000000..083dedbd11 --- /dev/null +++ b/libs/components/editor-core/src/editor/config/grid.ts @@ -0,0 +1,27 @@ +import { BlockEditor } from '../..'; + +/** + * + * the global config for the editor + * @class GridConfig + */ +export class GridConfig { + private _maxGridItemCount = 6; + private _editor: BlockEditor; + + constructor(editor: BlockEditor) { + this._editor = editor; + } + + get maxGridItemCount() { + return this._maxGridItemCount; + } + + set maxGridItemCount(value) { + this._maxGridItemCount = value; + } + + get gridItemMinWidth() { + return 100 / this.maxGridItemCount; + } +} diff --git a/libs/components/editor-core/src/editor/config/index.ts b/libs/components/editor-core/src/editor/config/index.ts new file mode 100644 index 0000000000..e7abe20d4a --- /dev/null +++ b/libs/components/editor-core/src/editor/config/index.ts @@ -0,0 +1,23 @@ +import { BlockEditor } from '../..'; +import { GridConfig } from './grid'; + +// TODO: if config be complex, add children config abstract +/** + * + * the global config for the editor + * @class EditorConfig + */ +export class EditorConfig { + private _maxGridItemCount = 6; + private _editor: BlockEditor; + private _grid: GridConfig; + + constructor(editor: BlockEditor) { + this._editor = editor; + this._grid = new GridConfig(editor); + } + + get grid() { + return this._grid; + } +} 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 35426c73de..eccdfb081e 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 @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import { domToRect, Point } from '@toeverything/utils'; import { AsyncBlock } from '../..'; import { GridDropType } from '../commands/types'; @@ -5,6 +6,7 @@ import { Editor } from '../editor'; import { BlockDropPlacement, GroupDirection } from '../types'; // TODO: Evaluate implementing custom events with Rxjs import EventEmitter from 'eventemitter3'; +import { Protocol } from '@toeverything/datasource/db-service'; enum DragType { dragBlock = 'dragBlock', @@ -86,6 +88,7 @@ export class DragDropManager { while (curr !== this._editor.getRootBlockId()) { if (curr === blockId) return false; const block = await this._editor.getBlockById(curr); + if (!block) return false; curr = block.parentId; } return true; @@ -114,6 +117,48 @@ export class DragDropManager { : GridDropType.right ); } + if ( + [ + BlockDropPlacement.outerLeft, + BlockDropPlacement.outerRight, + ].includes(this._blockDragDirection) + ) { + const targetBlock = await this._editor.getBlockById( + this._blockDragTargetId + ); + if (targetBlock.type !== Protocol.Block.Type.grid) { + await this._editor.commands.blockCommands.createLayoutBlock( + blockId, + this._blockDragTargetId, + this._blockDragDirection === + BlockDropPlacement.outerLeft + ? GridDropType.left + : GridDropType.right + ); + } + if (targetBlock.type === Protocol.Block.Type.grid) { + const gridItems = await targetBlock.children(); + if ( + BlockDropPlacement.outerRight === + this._blockDragDirection + ) { + await this._editor.commands.blockCommands.moveInNewGridItem( + blockId, + gridItems[gridItems.length - 1].id + ); + } + if ( + BlockDropPlacement.outerLeft === + this._blockDragDirection + ) { + await this._editor.commands.blockCommands.moveInNewGridItem( + blockId, + gridItems[0].id, + true + ); + } + } + } } } @@ -209,6 +254,93 @@ export class DragDropManager { ); } + /** + * + * check if drag block is out of blocks and return direction + * @param {React.DragEvent} event + * @return { + * direction: BlockDropPlacement.none, // none, outerLeft, outerRight + * block: undefined, // the block in the same clientY + * isOuter: false, // if is drag over outer + * } + * + * @memberof DragDropManager + */ + public async checkOuterBlockDragTypes(event: React.DragEvent) { + const { clientX, clientY } = event; + const mousePoint = new Point(clientX, clientY); + const rootBlock = await this._editor.getBlockById( + this._editor.getRootBlockId() + ); + let direction = BlockDropPlacement.none; + const rootBlockRect = domToRect(rootBlock.dom); + let targetBlock: AsyncBlock | undefined; + let typesInfo = { + direction: BlockDropPlacement.none, + block: undefined, + isOuter: false, + } as { + direction: BlockDropPlacement; + block: AsyncBlock | undefined; + isOuter: boolean; + }; + if (rootBlockRect.isPointLeft(mousePoint)) { + direction = BlockDropPlacement.outerLeft; + typesInfo.isOuter = true; + } + if (rootBlockRect.isPointRight(mousePoint)) { + direction = BlockDropPlacement.outerRight; + typesInfo.isOuter = true; + } + if (direction !== BlockDropPlacement.none) { + const blockList = await this._editor.getBlockListByLevelOrder(); + targetBlock = blockList.find(block => { + const domRect = domToRect(block.dom); + const pointChecker = + direction === BlockDropPlacement.outerLeft + ? domRect.isPointLeft.bind(domRect) + : domRect.isPointRight.bind(domRect); + return ( + block.type !== Protocol.Block.Type.page && + block.type !== Protocol.Block.Type.group && + pointChecker(mousePoint) + ); + }); + if (targetBlock) { + if (targetBlock.type !== Protocol.Block.Type.grid) { + this._setBlockDragDirection(direction); + this._setBlockDragTargetId(targetBlock.id); + typesInfo = { + direction, + block: targetBlock, + isOuter: true, + }; + } + if (targetBlock.type === Protocol.Block.Type.grid) { + const children = await targetBlock.children(); + if ( + children.length < + this._editor.configManager.grid.maxGridItemCount + ) { + typesInfo = { + direction, + block: targetBlock, + isOuter: true, + }; + } + } + } + } + if ( + typesInfo.direction !== BlockDropPlacement.none && + typesInfo.block + ) { + this._setBlockDragTargetId(targetBlock.id); + } + this._setBlockDragDirection(typesInfo.direction); + return typesInfo; + } + public async checkBlockDragTypes( event: React.DragEvent, blockDom: HTMLElement, @@ -216,10 +348,25 @@ export class DragDropManager { ) { const { clientX, clientY } = event; this._setBlockDragTargetId(blockId); - + const path = await this._editor.getBlockPath(blockId); const mousePoint = new Point(clientX, clientY); const rect = domToRect(blockDom); + /** + * IMP: compute the level of the target block + * future feature drag drop has level support do not delete + * const levelUnderGrid = Array.from(path) + .reverse() + .findIndex(block => block.type === Protocol.Block.Type.gridItem); + const levelUnderGroup = Array.from(path) + .reverse() + .findIndex(block => block.type === Protocol.Block.Type.group); + const blockLevel = + levelUnderGrid > 0 ? levelUnderGrid : levelUnderGroup; + console.log({ blockLevel, levelUnderGrid, levelUnderGroup }); + * + */ let direction = BlockDropPlacement.bottom; + if (mousePoint.x - rect.left <= this._dragBlockHotDistance) { direction = BlockDropPlacement.left; } @@ -236,9 +383,10 @@ export class DragDropManager { direction === BlockDropPlacement.left || direction === BlockDropPlacement.right ) { - const path = await this._editor.getBlockPath(blockId); - const gridBlocks = path.filter(block => block.type === 'grid'); - // limit grid block floor counts + const gridBlocks = path.filter( + block => block.type === Protocol.Block.Type.grid + ); + // limit grid block floor counts, when drag block to init grid if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) { direction = BlockDropPlacement.none; } diff --git a/libs/components/editor-core/src/editor/drag-drop/types.ts b/libs/components/editor-core/src/editor/drag-drop/types.ts index f7cc40133a..820370e1a6 100644 --- a/libs/components/editor-core/src/editor/drag-drop/types.ts +++ b/libs/components/editor-core/src/editor/drag-drop/types.ts @@ -3,6 +3,8 @@ export enum BlockDropPlacement { right = 'right', top = 'top', bottom = 'bottom', + outerLeft = 'outer-left', + outerRight = 'outer-right', none = 'none', } diff --git a/libs/components/editor-core/src/editor/editor.ts b/libs/components/editor-core/src/editor/editor.ts index 208c4e9ef1..24854735f4 100644 --- a/libs/components/editor-core/src/editor/editor.ts +++ b/libs/components/editor-core/src/editor/editor.ts @@ -35,6 +35,7 @@ import { BrowserClipboard } from './clipboard/browser-clipboard'; import { ClipboardPopulator } from './clipboard/clipboard-populator'; import { BlockHelper } from './block/block-helper'; import { DragDropManager } from './drag-drop'; +import { EditorConfig } from './config'; export interface EditorCtorProps { workspace: string; @@ -56,6 +57,7 @@ export class Editor implements Virgo { public dragDropManager = new DragDropManager(this); public commands = new EditorCommands(this); public blockHelper = new BlockHelper(this); + public configManager = new EditorConfig(this); public bdCommands: Commands; public ui_container?: HTMLDivElement; public version = '0.0.1'; @@ -343,6 +345,23 @@ export class Editor implements Virgo { return [...blockList, ...(await this.getOffspring(rootBlockId))]; } + async getBlockListByLevelOrder() { + const rootBlockId = this.getRootBlockId(); + const rootBlock = await this.getBlockById(rootBlockId); + const blockList: Array = []; + let nextToVisit: Array = rootBlock ? [rootBlock] : []; + while (nextToVisit.length) { + let next: Array = []; + for (const block of nextToVisit) { + const children = await block.children(); + blockList.push(block); + next = next.concat(children); + } + nextToVisit = next; + } + return blockList; + } + /** * * get all offspring of block diff --git a/libs/components/editor-core/src/editor/scroll/scroll.ts b/libs/components/editor-core/src/editor/scroll/scroll.ts index 2b2f1d114b..971e2ca471 100644 --- a/libs/components/editor-core/src/editor/scroll/scroll.ts +++ b/libs/components/editor-core/src/editor/scroll/scroll.ts @@ -110,7 +110,6 @@ export class ScrollManager { } public emitScrollEvent(event: UIEvent) { - this.scrollContainer = event.target as HTMLElement; this._scrollDirection = this._getScrollDirection(); this._scrollMoveOffset = Math.abs( this.scrollContainer.scrollTop - this._scrollRecord[0] diff --git a/libs/components/editor-core/src/editor/types.ts b/libs/components/editor-core/src/editor/types.ts index ca22ae5d4e..95cec2a76a 100644 --- a/libs/components/editor-core/src/editor/types.ts +++ b/libs/components/editor-core/src/editor/types.ts @@ -73,6 +73,7 @@ export interface Virgo { getBlockById(blockId: string): Promise; setHotKeysScope(scope?: string): void; getBlockList: () => Promise; + getBlockListByLevelOrder: () => Promise; // removeBlocks: () => void; storageManager: StorageManager | undefined; selection: VirgoSelection; 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 6c7e357b50..4843a24560 100644 --- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx +++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx @@ -52,7 +52,6 @@ function Line(props: { lineInfo: LineInfo; rootRect: DOMRect }) { return null; } const { direction, blockInfo } = lineInfo; - const finalDirection = direction; const lineStyle = { zIndex: 2, position: 'absolute' as const, @@ -91,14 +90,14 @@ function Line(props: { lineInfo: LineInfo; rootRect: DOMRect }) { left: intersectionRect.right + 10 - rootRect.x, }; const styleMap = { - left: leftLineStyle, - right: rightLineStyle, - top: topLineStyle, - bottom: bottomLineStyle, + [BlockDropPlacement.left]: leftLineStyle, + [BlockDropPlacement.right]: rightLineStyle, + [BlockDropPlacement.top]: topLineStyle, + [BlockDropPlacement.bottom]: bottomLineStyle, + [BlockDropPlacement.outerLeft]: leftLineStyle, + [BlockDropPlacement.outerRight]: rightLineStyle, }; - return ( -
- ); + return
; } function DragComponent(props: { 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 806c93c971..113dec843d 100644 --- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx +++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx @@ -5,8 +5,9 @@ import { ignoreBlockTypes } from './menu-config'; import { LineInfoSubject, LeftMenuDraggable } from './LeftMenuDraggable'; import { PluginRenderRoot } from '../../utils'; import { Subject } from 'rxjs'; -import { domToRect, last, Point } from '@toeverything/utils'; - +import { domToRect, last, Point, throttle } from '@toeverything/utils'; +import { BlockDropPlacement } from '@toeverything/framework/virgo'; +const DRAG_THROTTLE_DELAY = 150; export class LeftMenuPlugin extends BasePlugin { private _mousedown?: boolean; private _root?: PluginRenderRoot; @@ -35,11 +36,7 @@ export class LeftMenuPlugin extends BasePlugin { .get(HookType.ON_ROOTNODE_MOUSE_UP) .subscribe(this._handleMouseUp) ); - this.sub.add( - this.hooks - .get(HookType.ON_ROOTNODE_DRAG_OVER) - .subscribe(this._handleDragOverBlockNode) - ); + this.sub.add( this.hooks.get(HookType.ON_ROOTNODE_MOUSE_LEAVE).subscribe(() => { this._hideLeftMenu(); @@ -60,8 +57,47 @@ export class LeftMenuPlugin extends BasePlugin { this.sub.add( this.hooks.get(HookType.ON_ROOTNODE_DROP).subscribe(this._onDrop) ); + this.sub.add( + this.hooks.get(HookType.ON_ROOTNODE_DRAG_OVER).subscribe( + throttle( + this._handleRootNodeDragover.bind(this), + DRAG_THROTTLE_DELAY, + { + leading: true, + } + ) + ) + ); } + private _handleRootNodeDragover = async ( + event: React.DragEvent + ) => { + event.preventDefault(); + if (this.editor.dragDropManager.isDragBlock(event)) { + const { direction, block, isOuter } = + await this.editor.dragDropManager.checkOuterBlockDragTypes( + event + ); + if (direction !== BlockDropPlacement.none && block && block.dom) { + this._lineInfo.next({ + direction, + blockInfo: { + blockId: block.id, + dom: block.dom, + type: block.type, + rect: block.dom.getBoundingClientRect(), + properties: block.getProperties(), + }, + }); + } else if (!isOuter) { + this._handleDragOverBlockNode(event); + } else { + this._lineInfo.next(undefined); + } + } + }; + private _onDrop = () => { this._lineInfo.next(undefined); }; diff --git a/libs/utils/src/rect.ts b/libs/utils/src/rect.ts index d13b08fa77..91e4805c40 100644 --- a/libs/utils/src/rect.ts +++ b/libs/utils/src/rect.ts @@ -121,20 +121,20 @@ export class Rect { } } - isPointDown({ y }: Point) { - return this.bottom < y; + isPointDown({ x, y }: Point) { + return this.bottom < y && this.left <= x && this.right >= x; } - isPointUp({ y }: Point) { - return y < this.top; + isPointUp({ x, y }: Point) { + return y < this.top && this.left <= x && this.right >= x; } - isPointLeft({ x }: Point) { - return x < this.left; + isPointLeft({ x, y }: Point) { + return x < this.left && this.top <= y && this.bottom >= y; } - isPointRight({ x }: Point) { - return x > this.right; + isPointRight({ x, y }: Point) { + return x > this.right && this.top <= y && this.bottom >= y; } fromNewLeft(left: number) {