Merge branch 'master' into feat/readme
This commit is contained in:
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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,10 +135,9 @@ 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
|
||||||
// if drag out of startblock change selection type to block
|
// if drag out of startblock change selection type to block
|
||||||
if (endpointBlock?.id === startPointBlock.current.id) {
|
if (endpointBlock?.id === startPointBlock.current.id) {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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,10 +56,9 @@ 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) {
|
||||||
groupBlockOnDragOver = null;
|
groupBlockOnDragOver = null;
|
||||||
}
|
}
|
||||||
@@ -83,10 +79,9 @@ 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) {
|
||||||
groupBlockOnDrop = null;
|
groupBlockOnDrop = null;
|
||||||
}
|
}
|
||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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 (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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,33 +122,24 @@ 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, {
|
|
||||||
capture: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onDragStart = async (event: React.DragEvent<Element>) => {
|
|
||||||
editor.dragDropManager.isOnDrag = true;
|
|
||||||
if (block == null) return;
|
|
||||||
setRootRect(editor.container.getBoundingClientRect());
|
|
||||||
const dragImage = await editor.blockHelper.getBlockDragImg(
|
|
||||||
block.blockId
|
|
||||||
);
|
|
||||||
if (dragImage) {
|
|
||||||
event.dataTransfer.setDragImage(dragImage, -50, -10);
|
|
||||||
editor.dragDropManager.setDragBlockInfo(event, block.blockId);
|
|
||||||
}
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
onDragStart(event);
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
setVisible(false);
|
||||||
|
|
||||||
|
editor.dragDropManager.isOnDrag = true;
|
||||||
|
if (block == null) return;
|
||||||
|
setRootRect(editor.container.getBoundingClientRect());
|
||||||
|
const dragImage = await editor.blockHelper.getBlockDragImg(
|
||||||
|
block.blockId
|
||||||
|
);
|
||||||
|
if (dragImage) {
|
||||||
|
event.dataTransfer.setDragImage(dragImage, -50, -10);
|
||||||
|
editor.dragDropManager.setDragBlockInfo(event, block.blockId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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,28 +151,20 @@ 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
|
||||||
setBlock(block);
|
.pipe(
|
||||||
if (block != null) {
|
distinctUntilChanged(
|
||||||
setRootRect(editor.container.getBoundingClientRect());
|
(prev, curr) => prev?.blockId === curr?.blockId
|
||||||
setVisible(true);
|
)
|
||||||
}
|
)
|
||||||
});
|
.subscribe(block => {
|
||||||
|
setBlock(block);
|
||||||
|
if (block != null) {
|
||||||
|
setRootRect(editor.container.getBoundingClientRect());
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
return () => sub.unsubscribe();
|
return () => sub.unsubscribe();
|
||||||
}, [blockInfo, editor]);
|
}, [blockInfo, editor]);
|
||||||
|
|
||||||
|
|||||||
@@ -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,31 +101,40 @@ export class LeftMenuPlugin extends BasePlugin {
|
|||||||
this._hideLeftMenu();
|
this._hideLeftMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node.blockId !== this._preBlockId) {
|
const node = await this.editor.getBlockByPoint(
|
||||||
if (node.dom) {
|
new Point(event.clientX, event.clientY)
|
||||||
const mousePoint = new Point(e.clientX, e.clientY);
|
);
|
||||||
const children = await (
|
if (node == null || ignoreBlockTypes.includes(node.type)) {
|
||||||
await this.editor.getBlockById(node.blockId)
|
return;
|
||||||
).children();
|
}
|
||||||
// if mouse point is between the first and last child do not show left menu
|
if (node.dom) {
|
||||||
if (children.length) {
|
const mousePoint = new Point(event.clientX, event.clientY);
|
||||||
const firstChildren = children[0];
|
const children = await (
|
||||||
const lastChildren = last(children);
|
await this.editor.getBlockById(node.id)
|
||||||
if (firstChildren.dom && lastChildren.dom) {
|
).children();
|
||||||
const firstChildrenRect = domToRect(firstChildren.dom);
|
// if mouse point is between the first and last child do not show left menu
|
||||||
const lastChildrenRect = domToRect(lastChildren.dom);
|
if (children.length) {
|
||||||
if (
|
const firstChildren = children[0];
|
||||||
firstChildrenRect.top < mousePoint.y &&
|
const lastChildren = last(children);
|
||||||
lastChildrenRect.bottom > mousePoint.y
|
if (firstChildren.dom && lastChildren.dom) {
|
||||||
) {
|
const firstChildrenRect = domToRect(firstChildren.dom);
|
||||||
return;
|
const lastChildrenRect = domToRect(lastChildren.dom);
|
||||||
}
|
if (
|
||||||
|
firstChildrenRect.top < mousePoint.y &&
|
||||||
|
lastChildrenRect.bottom > mousePoint.y
|
||||||
|
) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._preBlockId = node.blockId;
|
|
||||||
this._showLeftMenu(node);
|
|
||||||
}
|
}
|
||||||
|
this._blockInfo.next({
|
||||||
|
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,
|
||||||
|
|||||||
Reference in New Issue
Block a user