Merge branch 'master' into feat/readme

This commit is contained in:
Chi Zhang
2022-08-01 18:14:33 +08:00
committed by GitHub
13 changed files with 216 additions and 221 deletions

View File

@@ -6,6 +6,7 @@ import type { DndableItems } from './type';
import type { import type {
KanbanCard, KanbanCard,
KanbanGroup, KanbanGroup,
RecastItem,
} from '@toeverything/components/editor-core'; } from '@toeverything/components/editor-core';
import { isEqual } from '@toeverything/utils'; import { isEqual } from '@toeverything/utils';
@@ -33,15 +34,44 @@ const findContainer = (id: string, items: DndableItems) => {
); );
}; };
type FindMoveInfo = (params: {
id: string;
activeContainer: string;
overContainer: string;
kanban: KanbanGroup[];
}) => {
targetCard: RecastItem;
targetGroup: KanbanGroup | null;
};
const findMoveInfo: FindMoveInfo = ({
id,
activeContainer,
overContainer,
kanban,
}) => {
const activeGroup = kanban.find(group => group.id === activeContainer);
const overGroup = kanban.find(group => group.id === overContainer);
const activityCard = activeGroup.items.find(item => item.id === id);
return {
targetCard: activityCard.block,
targetGroup: overGroup,
};
};
/** /**
* Find the sibling node after the dragging of the moved node ends * Find the sibling node after the dragging of the moved node ends
* @param cards * @param cards
* @param currentCardId * @param currentCardId
*/ */
const findSibling = (cards: KanbanCard[], currentCardId: string) => { const findSibling = (
cards: KanbanCard[],
currentCardId: string
): [string, string, number] => {
const index = cards.findIndex(card => card.id === currentCardId); const index = cards.findIndex(card => card.id === currentCardId);
return [cards[index - 1]?.id ?? null, cards[index + 1]?.id ?? null]; return [cards[index - 1]?.id ?? null, cards[index + 1]?.id ?? null, index];
}; };
/** /**
@@ -84,4 +114,5 @@ export {
findSibling, findSibling,
pickIdFromCards, pickIdFromCards,
shouldUpdate, shouldUpdate,
findMoveInfo,
}; };

View File

@@ -11,6 +11,7 @@ import {
findSibling, findSibling,
pickIdFromCards, pickIdFromCards,
shouldUpdate, shouldUpdate,
findMoveInfo,
} from '../helper'; } from '../helper';
import type { import type {
CollisionDetection, CollisionDetection,
@@ -19,11 +20,13 @@ import type {
DragEndEvent, DragEndEvent,
} from '@dnd-kit/core'; } from '@dnd-kit/core';
import type { DndableItems, UseDndableRes } from '../type'; import type { DndableItems, UseDndableRes } from '../type';
import { useKanban } from '@toeverything/components/editor-core';
export const useDndable = ( export const useDndable = (
dndableItems: DndableItems, dndableItems: DndableItems,
dndableContainerIds: string[] dndableContainerIds: string[]
): UseDndableRes => { ): UseDndableRes => {
const { kanban, moveCard } = useKanban();
const [items, setItems] = useState(dndableItems); const [items, setItems] = useState(dndableItems);
const [containerIds, setContainerIds] = useState(dndableContainerIds); const [containerIds, setContainerIds] = useState(dndableContainerIds);
const [active, setActive] = useState(null); const [active, setActive] = useState(null);
@@ -259,16 +262,14 @@ export const useDndable = (
).filter(Boolean), ).filter(Boolean),
}; };
const activeItems = items[activeContainer]; const { targetCard } = findMoveInfo({
const activeItem = activeItems.find( id: activeId,
item => item.id === activeId activeContainer,
); overContainer,
const [beforeId, afterId] = findSibling( kanban,
items[overContainer], });
activeId
);
activeItem?.moveTo(overContainer, beforeId, afterId); moveCard(targetCard, null, overIndex);
return data; return data;
}); });

View File

@@ -1,5 +1,4 @@
import type { BlockEditor } from './editor'; import type { BlockEditor } from './editor';
import { Point } from '@toeverything/utils';
import { styled, usePatchNodes } from '@toeverything/components/ui'; import { styled, usePatchNodes } from '@toeverything/components/ui';
import type { FC, PropsWithChildren } from 'react'; import type { FC, PropsWithChildren } from 'react';
import React, { useEffect, useRef, useState, useCallback } from 'react'; import React, { useEffect, useRef, useState, useCallback } from 'react';
@@ -76,20 +75,6 @@ export const RenderRoot: FC<PropsWithChildren<RenderRootProps>> = ({
) => { ) => {
selectionRef.current?.onMouseMove(event); selectionRef.current?.onMouseMove(event);
editor.getHooks().onRootNodeMouseMove(event); editor.getHooks().onRootNodeMouseMove(event);
const slidingBlock = await editor.getBlockByPoint(
new Point(event.clientX, event.clientY)
);
if (slidingBlock && slidingBlock.dom) {
editor.getHooks().afterOnNodeMouseMove(event, {
blockId: slidingBlock.id,
dom: slidingBlock.dom,
rect: slidingBlock.dom.getBoundingClientRect(),
type: slidingBlock.type,
properties: slidingBlock.getProperties(),
});
}
}; };
const onMouseDown = ( const onMouseDown = (
@@ -142,6 +127,12 @@ export const RenderRoot: FC<PropsWithChildren<RenderRootProps>> = ({
} }
}; };
const onDragLeave = (event: React.DragEvent<Element>) => {
if (editor.dragDropManager.isEnabled()) {
editor.getHooks().onRootNodeDragLeave(event);
}
};
const onDragOverCapture = (event: React.DragEvent<Element>) => { const onDragOverCapture = (event: React.DragEvent<Element>) => {
event.preventDefault(); event.preventDefault();
if (editor.dragDropManager.isEnabled()) { if (editor.dragDropManager.isEnabled()) {
@@ -178,6 +169,7 @@ export const RenderRoot: FC<PropsWithChildren<RenderRootProps>> = ({
onKeyDownCapture={onKeyDownCapture} onKeyDownCapture={onKeyDownCapture}
onKeyUp={onKeyUp} onKeyUp={onKeyUp}
onDragOver={onDragOver} onDragOver={onDragOver}
onDragLeave={onDragLeave}
onDragOverCapture={onDragOverCapture} onDragOverCapture={onDragOverCapture}
onDragEnd={onDragEnd} onDragEnd={onDragEnd}
onDrop={onDrop} onDrop={onDrop}

View File

@@ -119,9 +119,7 @@ export const SelectionRect = forwardRef<SelectionRef, SelectionProps>(
startPointRef.current = new Point(event.clientX, event.clientY); startPointRef.current = new Point(event.clientX, event.clientY);
startPointBlock.current = startPointBlock.current =
((await selectionManager.rootDomReady()) && ((await selectionManager.rootDomReady()) &&
(await selectionManager.getBlockByPoint( (await editor.getBlockByPoint(startPointRef.current))) ||
startPointRef.current
))) ||
null; null;
mouseType.current = 'down'; mouseType.current = 'down';
if (scrollManager.scrollContainer) { if (scrollManager.scrollContainer) {
@@ -137,8 +135,7 @@ export const SelectionRect = forwardRef<SelectionRef, SelectionProps>(
if (mouseType.current === 'down') { if (mouseType.current === 'down') {
endPointRef.current = new Point(event.clientX, event.clientY); endPointRef.current = new Point(event.clientX, event.clientY);
if (startPointBlock.current) { if (startPointBlock.current) {
const endpointBlock = const endpointBlock = await editor.getBlockByPoint(
await selectionManager.getBlockByPoint(
endPointRef.current endPointRef.current
); );
// TODO: delete after multi-block text selection done // TODO: delete after multi-block text selection done

View File

@@ -1,4 +1,4 @@
import { domToRect, Point, ValueOf } from '@toeverything/utils'; import { domToRect, Point } from '@toeverything/utils';
import { AsyncBlock } from '../..'; import { AsyncBlock } from '../..';
import { GridDropType } from '../commands/types'; import { GridDropType } from '../commands/types';
import { Editor } from '../editor'; import { Editor } from '../editor';
@@ -6,9 +6,10 @@ import { BlockDropPlacement, GroupDirection } from '../types';
// TODO: Evaluate implementing custom events with Rxjs // TODO: Evaluate implementing custom events with Rxjs
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
type DargType = enum DragType {
| ValueOf<InstanceType<typeof DragDropManager>['dragActions']> dragBlock = 'dragBlock',
| ''; dragGroup = 'dragGroup',
}
const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange'; const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange';
export class DragDropManager { export class DragDropManager {
@@ -18,16 +19,13 @@ export class DragDropManager {
private _blockIdKey = 'blockId'; private _blockIdKey = 'blockId';
private _rootIdKey = 'rootId'; private _rootIdKey = 'rootId';
private _dragType: DargType; private _dragType?: DragType;
private _blockDragDirection: BlockDropPlacement; private _blockDragDirection: BlockDropPlacement;
private _blockDragTargetId = ''; private _blockDragTargetId?: string;
private _dragBlockHotDistance = 20; private _dragBlockHotDistance = 20;
private _dragActions = { private _dragActions = DragType;
dragBlock: 'dragBlock',
dragGroup: 'dragGroup',
} as const;
private _isOnDrag = false; private _isOnDrag = false;
@@ -49,7 +47,6 @@ export class DragDropManager {
constructor(editor: Editor) { constructor(editor: Editor) {
this._editor = editor; this._editor = editor;
this._enabled = true; this._enabled = true;
this._dragType = '';
this._blockDragDirection = BlockDropPlacement.none; this._blockDragDirection = BlockDropPlacement.none;
this._initMouseEvent(); this._initMouseEvent();
} }
@@ -58,7 +55,7 @@ export class DragDropManager {
return this._dragType; return this._dragType;
} }
set dragType(type: DargType) { set dragType(type: DragType) {
this._dragType = type; this._dragType = type;
} }
@@ -119,22 +116,9 @@ export class DragDropManager {
} }
} }
public async getGroupBlockByPoint(point: Point) {
const blockList = await this._editor.getBlockList();
return blockList.find(block => {
if (block.type === 'group' && block.dom) {
const rect = domToRect(block.dom);
if (rect.fromNewLeft(rect.left - 30).isContainPoint(point)) {
return true;
}
}
return false;
});
}
private async _handleDropGroup(event: React.DragEvent<Element>) { private async _handleDropGroup(event: React.DragEvent<Element>) {
const blockId = event.dataTransfer.getData(this._blockIdKey); const blockId = event.dataTransfer.getData(this._blockIdKey);
const toGroup = await this.getGroupBlockByPoint( const toGroup = await this._editor.getGroupBlockByPoint(
new Point(event.clientX, event.clientY) new Point(event.clientX, event.clientY)
); );
if (toGroup && blockId && toGroup.id !== blockId) { if (toGroup && blockId && toGroup.id !== blockId) {
@@ -262,7 +246,7 @@ export class DragDropManager {
this._handleDropGroup(event); this._handleDropGroup(event);
} }
} }
this.dragType = ''; this.dragType = undefined;
} }
public handlerEditorDragOver(event: React.DragEvent<Element>) { public handlerEditorDragOver(event: React.DragEvent<Element>) {
@@ -280,9 +264,14 @@ export class DragDropManager {
} }
private _resetDragDropData() { private _resetDragDropData() {
this._dragType = ''; this._dragType = undefined;
this._setBlockDragDirection(BlockDropPlacement.none); this._setBlockDragDirection(BlockDropPlacement.none);
this._setBlockDragTargetId(''); this._setBlockDragTargetId(undefined);
}
public clearDropInfo() {
this._setBlockDragDirection(BlockDropPlacement.none);
this._setBlockDragTargetId(undefined);
} }
public async checkDragGroupDirection( public async checkDragGroupDirection(

View File

@@ -389,6 +389,19 @@ export class Editor implements Virgo {
}); });
} }
public async getGroupBlockByPoint(point: Point) {
const blockList = await this.getBlockList();
return blockList.find(block => {
if (block.type === 'group' && block.dom) {
const rect = domToRect(block.dom);
if (rect.fromNewLeft(rect.left - 30).isContainPoint(point)) {
return true;
}
}
return false;
});
}
async undo() { async undo() {
await services.api.editorBlock.undo(this.workspace); await services.api.editorBlock.undo(this.workspace);
} }

View File

@@ -1,3 +1,4 @@
import { DragEvent } from 'react';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { HooksRunner, HookType, BlockDomInfo, PluginHooks } from '../types'; import { HooksRunner, HookType, BlockDomInfo, PluginHooks } from '../types';
@@ -86,13 +87,6 @@ export class Hooks implements HooksRunner, PluginHooks {
this._runHook(HookType.ON_ROOTNODE_MOUSE_LEAVE, e); this._runHook(HookType.ON_ROOTNODE_MOUSE_LEAVE, e);
} }
public afterOnNodeMouseMove(
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
node: BlockDomInfo
): void {
this._runHook(HookType.AFTER_ON_NODE_MOUSE_MOVE, e, node);
}
public afterOnResize( public afterOnResize(
e: React.MouseEvent<HTMLDivElement, MouseEvent> e: React.MouseEvent<HTMLDivElement, MouseEvent>
): void { ): void {
@@ -103,6 +97,10 @@ export class Hooks implements HooksRunner, PluginHooks {
this._runHook(HookType.ON_ROOTNODE_DRAG_OVER, e); this._runHook(HookType.ON_ROOTNODE_DRAG_OVER, e);
} }
public onRootNodeDragLeave(e: React.DragEvent<Element>): void {
this._runHook(HookType.ON_ROOTNODE_DRAG_LEAVE, e);
}
public onRootNodeDragEnd(e: React.DragEvent<Element>): void { public onRootNodeDragEnd(e: React.DragEvent<Element>): void {
this._runHook(HookType.ON_ROOTNODE_DRAG_END, e); this._runHook(HookType.ON_ROOTNODE_DRAG_END, e);
} }

View File

@@ -293,21 +293,8 @@ export class SelectionManager implements VirgoSelection {
return Boolean(rootBlock?.dom); return Boolean(rootBlock?.dom);
} }
public async getBlockByPoint(point: Point) {
const blockList = await this._editor.getBlockList();
const outBlockList = blockList.filter(block => {
return (
Boolean(block.dom) && domToRect(block.dom).isContainPoint(point)
);
});
return outBlockList.length
? outBlockList[outBlockList.length - 1]
: undefined;
}
public async isPointInBlocks(point: Point) { public async isPointInBlocks(point: Point) {
return Boolean(this.getBlockByPoint(point)); return Boolean(this._editor.getBlockByPoint(point));
} }
/** /**

View File

@@ -22,6 +22,7 @@ import type { BlockCommands } from './commands/block-commands';
import type { DragDropManager } from './drag-drop'; import type { DragDropManager } from './drag-drop';
import { MouseManager } from './mouse'; import { MouseManager } from './mouse';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Point } from '@toeverything/utils';
// import { BrowserClipboard } from './clipboard/browser-clipboard'; // import { BrowserClipboard } from './clipboard/browser-clipboard';
@@ -105,6 +106,8 @@ export interface Virgo {
// clipboard: BrowserClipboard; // clipboard: BrowserClipboard;
workspace: string; workspace: string;
getBlockDomById: (id: string) => Promise<HTMLElement>; getBlockDomById: (id: string) => Promise<HTMLElement>;
getBlockByPoint: (point: Point) => Promise<AsyncBlock>;
getGroupBlockByPoint: (point: Point) => Promise<AsyncBlock>;
isWhiteboard: boolean; isWhiteboard: boolean;
mouseManager: MouseManager; mouseManager: MouseManager;
} }
@@ -166,9 +169,9 @@ export enum HookType {
ON_ROOTNODE_MOUSE_OUT = 'onRootNodeMouseOut', ON_ROOTNODE_MOUSE_OUT = 'onRootNodeMouseOut',
ON_ROOTNODE_MOUSE_LEAVE = 'onRootNodeMouseLeave', ON_ROOTNODE_MOUSE_LEAVE = 'onRootNodeMouseLeave',
ON_SEARCH = 'onSearch', ON_SEARCH = 'onSearch',
AFTER_ON_NODE_MOUSE_MOVE = 'afterOnNodeMouseMove',
AFTER_ON_RESIZE = 'afterOnResize', AFTER_ON_RESIZE = 'afterOnResize',
ON_ROOTNODE_DRAG_OVER = 'onRootNodeDragOver', ON_ROOTNODE_DRAG_OVER = 'onRootNodeDragOver',
ON_ROOTNODE_DRAG_LEAVE = 'onRootNodeDragLeave',
ON_ROOTNODE_DRAG_END = 'onRootNodeDragEnd', ON_ROOTNODE_DRAG_END = 'onRootNodeDragEnd',
ON_ROOTNODE_DRAG_OVER_CAPTURE = 'onRootNodeDragOverCapture', ON_ROOTNODE_DRAG_OVER_CAPTURE = 'onRootNodeDragOverCapture',
ON_ROOTNODE_DROP = 'onRootNodeDrop', ON_ROOTNODE_DROP = 'onRootNodeDrop',
@@ -209,13 +212,10 @@ export interface HooksRunner {
e: React.MouseEvent<HTMLDivElement, MouseEvent> e: React.MouseEvent<HTMLDivElement, MouseEvent>
) => void; ) => void;
onSearch: () => void; onSearch: () => void;
afterOnNodeMouseMove: (
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
node: BlockDomInfo
) => void;
afterOnResize: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void; afterOnResize: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
onRootNodeDragOver: (e: React.DragEvent<Element>) => void; onRootNodeDragOver: (e: React.DragEvent<Element>) => void;
onRootNodeDragEnd: (e: React.DragEvent<Element>) => void; onRootNodeDragEnd: (e: React.DragEvent<Element>) => void;
onRootNodeDragLeave: (e: React.DragEvent<Element>) => void;
onRootNodeDrop: (e: React.DragEvent<Element>) => void; onRootNodeDrop: (e: React.DragEvent<Element>) => void;
afterOnNodeDragOver: ( afterOnNodeDragOver: (
e: React.DragEvent<Element>, e: React.DragEvent<Element>,

View File

@@ -28,14 +28,11 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) {
const handleRootMouseMove = useCallback( const handleRootMouseMove = useCallback(
async (e: React.MouseEvent<HTMLDivElement>) => { async (e: React.MouseEvent<HTMLDivElement>) => {
const groupBlockNew = const groupBlockNew = await editor.getGroupBlockByPoint(
await editor.dragDropManager.getGroupBlockByPoint(
new Point(e.clientX, e.clientY) new Point(e.clientX, e.clientY)
); );
if (groupBlockNew) { if (groupBlockNew) {
setGroupBlock(groupBlockNew); setGroupBlock(groupBlockNew);
} else {
setGroupBlock(null);
} }
}, },
[editor, setGroupBlock] [editor, setGroupBlock]
@@ -59,8 +56,7 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) {
let groupBlockOnDragOver = null; let groupBlockOnDragOver = null;
const mousePoint = new Point(e.clientX, e.clientY); const mousePoint = new Point(e.clientX, e.clientY);
if (editor.dragDropManager.isDragGroup(e)) { if (editor.dragDropManager.isDragGroup(e)) {
groupBlockOnDragOver = groupBlockOnDragOver = await editor.getGroupBlockByPoint(
await editor.dragDropManager.getGroupBlockByPoint(
mousePoint mousePoint
); );
if (groupBlockOnDragOver?.id === groupBlock?.id) { if (groupBlockOnDragOver?.id === groupBlock?.id) {
@@ -83,8 +79,7 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) {
async (e: React.DragEvent<Element>) => { async (e: React.DragEvent<Element>) => {
let groupBlockOnDrop = null; let groupBlockOnDrop = null;
if (editor.dragDropManager.isDragGroup(e)) { if (editor.dragDropManager.isDragGroup(e)) {
groupBlockOnDrop = groupBlockOnDrop = await editor.getGroupBlockByPoint(
await editor.dragDropManager.getGroupBlockByPoint(
new Point(e.clientX, e.clientY) new Point(e.clientX, e.clientY)
); );
if (groupBlockOnDrop?.id === groupBlock?.id) { if (groupBlockOnDrop?.id === groupBlock?.id) {
@@ -95,6 +90,8 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) {
[editor, groupBlock] [editor, groupBlock]
); );
const handleRootMouseLeave = useCallback(() => setGroupBlock(null), []);
const handleRootDragEnd = () => { const handleRootDragEnd = () => {
setDragOverGroup(null); setDragOverGroup(null);
}; };
@@ -118,6 +115,11 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) {
.get(HookType.ON_ROOTNODE_DRAG_END) .get(HookType.ON_ROOTNODE_DRAG_END)
.subscribe(handleRootDragEnd) .subscribe(handleRootDragEnd)
); );
sub.add(
hooks
.get(HookType.ON_ROOTNODE_MOUSE_LEAVE)
.subscribe(handleRootMouseLeave)
);
return () => { return () => {
sub.unsubscribe(); sub.unsubscribe();
}; };
@@ -127,6 +129,7 @@ export const GroupMenu = function ({ editor, hooks }: GroupMenuProps) {
handleRootMouseDown, handleRootMouseDown,
handleRootDragOver, handleRootDragOver,
handleRootDrop, handleRootDrop,
handleRootMouseLeave,
]); ]);
useEffect(() => { useEffect(() => {

View File

@@ -55,36 +55,36 @@ export function LeftMenu(props: LeftMenuProps) {
const [menuList, setMenuList] = useState<CascaderItemProps[]>(menu); const [menuList, setMenuList] = useState<CascaderItemProps[]>(menu);
const filter_items = ( // const filterItems = (
value: string, // value: string,
menuList: CascaderItemProps[], // menuList: CascaderItemProps[],
filterList: CascaderItemProps[] // filterList: CascaderItemProps[]
) => { // ) => {
menuList.forEach(item => { // menuList.forEach(item => {
if (item?.subItems.length === 0) { // if (item?.subItems.length === 0) {
if (item.title.toLocaleLowerCase().indexOf(value) !== -1) { // if (item.title.toLocaleLowerCase().indexOf(value) !== -1) {
filterList.push(item); // filterList.push(item);
} // }
} else { // } else {
filter_items(value, item.subItems || [], filterList); // filterItems(value, item.subItems || [], filterList);
} // }
}); // });
}; // };
const on_filter = ( // const on_filter = (
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement> // e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => { // ) => {
const value = e.currentTarget.value; // const value = e.currentTarget.value;
if (!value) { // if (!value) {
setMenuList(menu); // setMenuList(menu);
} else { // } else {
const filterList: CascaderItemProps[] = []; // const filterList: CascaderItemProps[] = [];
filter_items(value.toLocaleLowerCase(), menu, filterList); // filter_items(value.toLocaleLowerCase(), menu, filterList);
setMenuList( // setMenuList(
filterList.length > 0 ? filterList : [{ title: 'No Result' }] // filterList.length > 0 ? filterList : [{ title: 'No Result' }]
); // );
} // }
}; // };
return ( return (
<> <>

View File

@@ -10,8 +10,7 @@ import { Button } from '@toeverything/components/common';
import { styled } from '@toeverything/components/ui'; import { styled } from '@toeverything/components/ui';
import { LeftMenu } from './LeftMenu'; import { LeftMenu } from './LeftMenu';
import { debounce } from '@toeverything/utils'; import { distinctUntilChanged, Subject } from 'rxjs';
import type { Subject } from 'rxjs';
import { HandleChildIcon } from '@toeverything/components/icons'; import { HandleChildIcon } from '@toeverything/components/icons';
import { MENU_WIDTH } from './menu-config'; import { MENU_WIDTH } from './menu-config';
@@ -123,12 +122,10 @@ export const LeftMenuDraggable: FC<LeftMenuProps> = props => {
const [block, setBlock] = useState<BlockDomInfo | undefined>(); const [block, setBlock] = useState<BlockDomInfo | undefined>();
const [line, setLine] = useState<LineInfo | undefined>(undefined); const [line, setLine] = useState<LineInfo | undefined>(undefined);
const handleDragStart = (event: React.DragEvent<Element>) => { const handleDragStart = async (event: React.DragEvent<Element>) => {
window.addEventListener('dragover', handleDragOverCapture, { event.stopPropagation();
capture: true, setVisible(false);
});
const onDragStart = async (event: React.DragEvent<Element>) => {
editor.dragDropManager.isOnDrag = true; editor.dragDropManager.isOnDrag = true;
if (block == null) return; if (block == null) return;
setRootRect(editor.container.getBoundingClientRect()); setRootRect(editor.container.getBoundingClientRect());
@@ -139,17 +136,10 @@ export const LeftMenuDraggable: FC<LeftMenuProps> = props => {
event.dataTransfer.setDragImage(dragImage, -50, -10); event.dataTransfer.setDragImage(dragImage, -50, -10);
editor.dragDropManager.setDragBlockInfo(event, block.blockId); editor.dragDropManager.setDragBlockInfo(event, block.blockId);
} }
setVisible(false);
};
onDragStart(event);
event.stopPropagation();
}; };
const handleDragEnd = (event: React.DragEvent<Element>) => { const handleDragEnd = (event: React.DragEvent<Element>) => {
event.preventDefault(); event.preventDefault();
window.removeEventListener('dragover', handleDragOverCapture, {
capture: true,
});
setLine(undefined); setLine(undefined);
}; };
@@ -161,22 +151,14 @@ export const LeftMenuDraggable: FC<LeftMenuProps> = props => {
setAnchorEl(currentTarget); setAnchorEl(currentTarget);
}; };
/**
* clear line info
*/
const handleDragOverCapture = debounce((e: MouseEvent) => {
const { target } = e;
if (
target instanceof HTMLElement &&
(!target.closest('[data-block-id]') ||
!editor.container.contains(target))
) {
setLine(undefined);
}
}, 10);
useEffect(() => { useEffect(() => {
const sub = blockInfo.subscribe(block => { const sub = blockInfo
.pipe(
distinctUntilChanged(
(prev, curr) => prev?.blockId === curr?.blockId
)
)
.subscribe(block => {
setBlock(block); setBlock(block);
if (block != null) { if (block != null) {
setRootRect(editor.container.getBoundingClientRect()); setRootRect(editor.container.getBoundingClientRect());

View File

@@ -10,7 +10,6 @@ import { domToRect, last, Point } from '@toeverything/utils';
export class LeftMenuPlugin extends BasePlugin { export class LeftMenuPlugin extends BasePlugin {
private _mousedown?: boolean; private _mousedown?: boolean;
private _root?: PluginRenderRoot; private _root?: PluginRenderRoot;
private _preBlockId: string;
private _hideTimer: number; private _hideTimer: number;
private _blockInfo: Subject<BlockDomInfo | undefined> = new Subject(); private _blockInfo: Subject<BlockDomInfo | undefined> = new Subject();
@@ -23,7 +22,7 @@ export class LeftMenuPlugin extends BasePlugin {
public override init(): void { public override init(): void {
this.sub.add( this.sub.add(
this.hooks this.hooks
.get(HookType.AFTER_ON_NODE_MOUSE_MOVE) .get(HookType.ON_ROOTNODE_MOUSE_MOVE)
.subscribe(this._handleMouseMove) .subscribe(this._handleMouseMove)
); );
this.sub.add( this.sub.add(
@@ -31,11 +30,6 @@ export class LeftMenuPlugin extends BasePlugin {
.get(HookType.ON_ROOTNODE_MOUSE_DOWN) .get(HookType.ON_ROOTNODE_MOUSE_DOWN)
.subscribe(this._handleMouseDown) .subscribe(this._handleMouseDown)
); );
this.sub.add(
this.hooks
.get(HookType.ON_ROOTNODE_MOUSE_LEAVE)
.subscribe(this._hideLeftMenu)
);
this.sub.add( this.sub.add(
this.hooks this.hooks
.get(HookType.ON_ROOTNODE_MOUSE_UP) .get(HookType.ON_ROOTNODE_MOUSE_UP)
@@ -46,10 +40,22 @@ export class LeftMenuPlugin extends BasePlugin {
.get(HookType.AFTER_ON_NODE_DRAG_OVER) .get(HookType.AFTER_ON_NODE_DRAG_OVER)
.subscribe(this._handleDragOverBlockNode) .subscribe(this._handleDragOverBlockNode)
); );
this.sub.add(
this.hooks.get(HookType.ON_ROOTNODE_MOUSE_LEAVE).subscribe(() => {
this._hideLeftMenu();
this._lineInfo.next(undefined);
})
);
this.sub.add(
this.hooks.get(HookType.ON_ROOTNODE_DRAG_LEAVE).subscribe(() => {
this.editor.dragDropManager.clearDropInfo();
this._lineInfo.next(undefined);
})
);
this.sub.add( this.sub.add(
this.hooks this.hooks
.get(HookType.ON_ROOT_NODE_KEYDOWN) .get(HookType.ON_ROOT_NODE_KEYDOWN)
.subscribe(this._handleKeyDown) .subscribe(this._hideLeftMenu)
); );
this.sub.add( this.sub.add(
this.hooks.get(HookType.ON_ROOTNODE_DROP).subscribe(this._onDrop) this.hooks.get(HookType.ON_ROOTNODE_DROP).subscribe(this._onDrop)
@@ -57,7 +63,6 @@ export class LeftMenuPlugin extends BasePlugin {
} }
private _onDrop = () => { private _onDrop = () => {
this._preBlockId = '';
this._lineInfo.next(undefined); this._lineInfo.next(undefined);
}; };
private _handleDragOverBlockNode = async ([event, blockInfo]: [ private _handleDragOverBlockNode = async ([event, blockInfo]: [
@@ -80,10 +85,9 @@ export class LeftMenuPlugin extends BasePlugin {
} }
}; };
private _handleMouseMove = async ([e, node]: [ private _handleMouseMove = async (
React.MouseEvent<HTMLDivElement, MouseEvent>, event: React.MouseEvent<HTMLDivElement, MouseEvent>
BlockDomInfo ) => {
]) => {
if (!this._hideTimer) { if (!this._hideTimer) {
this._hideTimer = window.setTimeout(() => { this._hideTimer = window.setTimeout(() => {
if (this._mousedown) { if (this._mousedown) {
@@ -97,11 +101,16 @@ export class LeftMenuPlugin extends BasePlugin {
this._hideLeftMenu(); this._hideLeftMenu();
return; return;
} }
if (node.blockId !== this._preBlockId) { const node = await this.editor.getBlockByPoint(
new Point(event.clientX, event.clientY)
);
if (node == null || ignoreBlockTypes.includes(node.type)) {
return;
}
if (node.dom) { if (node.dom) {
const mousePoint = new Point(e.clientX, e.clientY); const mousePoint = new Point(event.clientX, event.clientY);
const children = await ( const children = await (
await this.editor.getBlockById(node.blockId) await this.editor.getBlockById(node.id)
).children(); ).children();
// if mouse point is between the first and last child do not show left menu // if mouse point is between the first and last child do not show left menu
if (children.length) { if (children.length) {
@@ -119,9 +128,13 @@ export class LeftMenuPlugin extends BasePlugin {
} }
} }
} }
this._preBlockId = node.blockId; this._blockInfo.next({
this._showLeftMenu(node); blockId: node.id,
} dom: node.dom,
rect: node.dom.getBoundingClientRect(),
type: node.type,
properties: node.getProperties(),
});
}; };
private _handleMouseUp() { private _handleMouseUp() {
@@ -140,17 +153,6 @@ export class LeftMenuPlugin extends BasePlugin {
this._blockInfo.next(undefined); this._blockInfo.next(undefined);
}; };
private _handleKeyDown = () => {
this._hideLeftMenu();
};
private _showLeftMenu = (blockInfo: BlockDomInfo): void => {
if (ignoreBlockTypes.includes(blockInfo.type)) {
return;
}
this._blockInfo.next(blockInfo);
};
protected override _onRender(): void { protected override _onRender(): void {
this._root = new PluginRenderRoot({ this._root = new PluginRenderRoot({
name: LeftMenuPlugin.pluginName, name: LeftMenuPlugin.pluginName,