feat: outer drag 、fix new page selection

This commit is contained in:
SaikaSakura 2022-08-05 19:29:26 +08:00
parent 812af254aa
commit 5c51457761
16 changed files with 358 additions and 72 deletions

View File

@ -124,7 +124,12 @@ const EditorContainer = ({
return ( return (
<StyledEditorContainer <StyledEditorContainer
lockScroll={lockScroll} lockScroll={lockScroll}
ref={scrollContainerRef} ref={ref => {
scrollContainerRef.current = ref;
if (editorRef.current?.scrollManager) {
editorRef.current.scrollManager.scrollContainer = ref;
}
}}
onScroll={onScroll} onScroll={onScroll}
> >
{pageId ? ( {pageId ? (

View File

@ -2,13 +2,13 @@ import { FC, useEffect, useLayoutEffect, useRef } from 'react';
import { ChildrenView } from '@toeverything/framework/virgo'; import { ChildrenView } from '@toeverything/framework/virgo';
import { styled } from '@toeverything/components/ui'; import { styled } from '@toeverything/components/ui';
import { sleep } from '@toeverything/utils'; 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_CLASS_NAME = 'grid-item';
export const GRID_ITEM_CONTENT_CLASS_NAME = `${GRID_ITEM_CLASS_NAME}-content`; export const GRID_ITEM_CONTENT_CLASS_NAME = `${GRID_ITEM_CLASS_NAME}-content`;
export const GridItem: FC<ChildrenView> = function (props) { export const GridItem: FC<ChildrenView> = function (props) {
const { children, block } = props; const { children, block, editor } = props;
const RENDER_DELAY_TIME = 100; const RENDER_DELAY_TIME = 100;
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>();
@ -25,6 +25,7 @@ export const GridItem: FC<ChildrenView> = function (props) {
const checkAndRefreshWidth = async () => { const checkAndRefreshWidth = async () => {
const currentWidth = block.getProperty(GRID_PROPERTY_KEY); const currentWidth = block.getProperty(GRID_PROPERTY_KEY);
const gridItemMinWidth = editor.configManager.grid.gridItemMinWidth;
if (currentWidth) { if (currentWidth) {
setWidth(currentWidth); setWidth(currentWidth);
} else if (!block.dom?.style.width) { } else if (!block.dom?.style.width) {
@ -64,26 +65,23 @@ export const GridItem: FC<ChildrenView> = function (props) {
if new width less then min width, if new width less then min width,
set min width and next block will be fix width set min width and next block will be fix width
*/ */
if (newWidth < GRID_ITEM_MIN_WIDTH) { if (newWidth < gridItemMinWidth) {
needFixWidth += GRID_ITEM_MIN_WIDTH - newWidth; needFixWidth += gridItemMinWidth - newWidth;
newWidth = GRID_ITEM_MIN_WIDTH; newWidth = gridItemMinWidth;
} }
// if can fix width, fix width // if can fix width, fix width
if ( if (newWidth > gridItemMinWidth && needFixWidth) {
newWidth > GRID_ITEM_MIN_WIDTH &&
needFixWidth
) {
if ( if (
newWidth - needFixWidth >= newWidth - needFixWidth >=
GRID_ITEM_MIN_WIDTH gridItemMinWidth
) { ) {
newWidth = newWidth - needFixWidth; newWidth = newWidth - needFixWidth;
needFixWidth = 0; needFixWidth = 0;
} else { } else {
needFixWidth = needFixWidth =
needFixWidth - needFixWidth -
(newWidth - GRID_ITEM_MIN_WIDTH); (newWidth - gridItemMinWidth);
newWidth = GRID_ITEM_MIN_WIDTH; newWidth = gridItemMinWidth;
} }
} }
if (index === children.length - 2) { if (index === children.length - 2) {

View File

@ -12,10 +12,8 @@ import { debounce, domToRect, Point } from '@toeverything/utils';
import clsx from 'clsx'; import clsx from 'clsx';
import { Protocol } from '@toeverything/datasource/db-service'; import { Protocol } from '@toeverything/datasource/db-service';
const MAX_ITEM_COUNT = 6;
const DB_UPDATE_DELAY = 50; const DB_UPDATE_DELAY = 50;
const GRID_ON_DRAG_CLASS = 'grid-layout-on-drag'; 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 const GRID_PROPERTY_KEY = 'gridItemWidth';
export function removePercent(str: string) { export function removePercent(str: string) {
@ -24,13 +22,14 @@ export function removePercent(str: string) {
export const Grid: FC<CreateView> = function (props) { export const Grid: FC<CreateView> = function (props) {
const { block, editor } = props; const { block, editor } = props;
const gridItemMinWidth = editor.configManager.grid.gridItemMinWidth;
const [isOnDrag, setIsOnDrag] = useState<boolean>(false); const [isOnDrag, setIsOnDrag] = useState<boolean>(false);
const isSetMouseUp = useRef<boolean>(false); const isSetMouseUp = useRef<boolean>(false);
const gridContainerRef = useRef<HTMLDivElement>(); const gridContainerRef = useRef<HTMLDivElement>();
const mouseStartPoint = useRef<Point>(); const mouseStartPoint = useRef<Point>();
const gridItemCountRef = useRef<number>(); const gridItemCountRef = useRef<number>();
const originalLeftWidth = useRef<number>(GRID_ITEM_MIN_WIDTH); const originalLeftWidth = useRef<number>(gridItemMinWidth);
const originalRightWidth = useRef<number>(GRID_ITEM_MIN_WIDTH); const originalRightWidth = useRef<number>(gridItemMinWidth);
const [alertHandleId, setAlertHandleId] = useState<string>(null); const [alertHandleId, setAlertHandleId] = useState<string>(null);
const getLeftRightGridItemDomByIndex = (index: number) => { const getLeftRightGridItemDomByIndex = (index: number) => {
@ -126,8 +125,8 @@ export const Grid: FC<CreateView> = function (props) {
editor.mouseManager.onMouseupEventOnce(() => { editor.mouseManager.onMouseupEventOnce(() => {
setIsOnDrag(false); setIsOnDrag(false);
isSetMouseUp.current = false; isSetMouseUp.current = false;
originalLeftWidth.current = GRID_ITEM_MIN_WIDTH; originalLeftWidth.current = gridItemMinWidth;
originalRightWidth.current = GRID_ITEM_MIN_WIDTH; originalRightWidth.current = gridItemMinWidth;
mouseStartPoint.current = null; mouseStartPoint.current = null;
}); });
} else { } else {
@ -153,12 +152,12 @@ export const Grid: FC<CreateView> = function (props) {
const newLeftWidth = originalLeftWidth.current - xDistance; const newLeftWidth = originalLeftWidth.current - xDistance;
let newLeftPercent = (newLeftWidth / containerWidth) * 100; let newLeftPercent = (newLeftWidth / containerWidth) * 100;
let newRightPercent = Number(totalWidth) - newLeftPercent; let newRightPercent = Number(totalWidth) - newLeftPercent;
if (newLeftPercent < GRID_ITEM_MIN_WIDTH) { if (newLeftPercent < gridItemMinWidth) {
newLeftPercent = GRID_ITEM_MIN_WIDTH; newLeftPercent = gridItemMinWidth;
newRightPercent = totalWidth - GRID_ITEM_MIN_WIDTH; newRightPercent = totalWidth - gridItemMinWidth;
} else if (newRightPercent < GRID_ITEM_MIN_WIDTH) { } else if (newRightPercent < gridItemMinWidth) {
newRightPercent = GRID_ITEM_MIN_WIDTH; newRightPercent = gridItemMinWidth;
newLeftPercent = totalWidth - GRID_ITEM_MIN_WIDTH; newLeftPercent = totalWidth - gridItemMinWidth;
} }
//XXX first change dom style is for animation speed, maybe not a good idea //XXX first change dom style is for animation speed, maybe not a good idea
const newLeft = `${newLeftPercent}%`; const newLeft = `${newLeftPercent}%`;
@ -213,6 +212,7 @@ export const Grid: FC<CreateView> = function (props) {
<GridContainer <GridContainer
className={clsx({ [GRID_ON_DRAG_CLASS]: isOnDrag })} className={clsx({ [GRID_ON_DRAG_CLASS]: isOnDrag })}
ref={gridContainerRef} ref={gridContainerRef}
gridItemMinWidth={gridItemMinWidth}
isOnDrag={isOnDrag} isOnDrag={isOnDrag}
> >
{block.childrenIds.map((id, i) => { {block.childrenIds.map((id, i) => {
@ -233,7 +233,8 @@ export const Grid: FC<CreateView> = function (props) {
onMouseDown={event => handleMouseDown(event, i)} onMouseDown={event => handleMouseDown(event, i)}
blockId={id} blockId={id}
enabledAddItem={ enabledAddItem={
block.childrenIds.length < MAX_ITEM_COUNT block.childrenIds.length <
editor.configManager.grid.maxGridItemCount
} }
onMouseEnter={event => onMouseEnter={event =>
handleHandleMouseEnter(event, i) handleHandleMouseEnter(event, i)
@ -252,24 +253,25 @@ export const Grid: FC<CreateView> = function (props) {
); );
}; };
const GridContainer = styled('div')<{ isOnDrag: boolean }>( const GridContainer = styled('div')<{
({ isOnDrag, theme }) => ({ isOnDrag: boolean;
position: 'relative', gridItemMinWidth: number;
display: 'flex', }>(({ isOnDrag, theme, gridItemMinWidth }) => ({
alignItems: 'stretch', position: 'relative',
borderRadius: '10px', display: 'flex',
border: '1px solid #FFF', alignItems: 'stretch',
minWidth: `${GRID_ITEM_MIN_WIDTH}%`, borderRadius: '10px',
[`&:hover .${GRID_ITEM_CONTENT_CLASS_NAME}`]: { 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, borderColor: theme.affine.palette.borderColor,
}, },
...(isOnDrag && { }),
[`& .${GRID_ITEM_CONTENT_CLASS_NAME}`]: { }));
borderColor: theme.affine.palette.borderColor,
},
}),
})
);
const GridMask = styled('div')({ const GridMask = styled('div')({
position: 'fixed', position: 'fixed',

View File

@ -3,7 +3,7 @@ import { Protocol } from '@toeverything/datasource/db-service';
import { AsyncBlock, BaseView } from '@toeverything/framework/virgo'; import { AsyncBlock, BaseView } from '@toeverything/framework/virgo';
import { GridItem } from '../grid-item/GridItem'; import { GridItem } from '../grid-item/GridItem';
import { GridRender } from './GridRender'; 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 { export class GridBlock extends BaseView {
public override selectable = false; public override selectable = false;

View File

@ -187,7 +187,6 @@ export const SelectionRect = forwardRef<SelectionRef, SelectionProps>(
) )
) )
); );
const scrollDirections = getScrollDirections( const scrollDirections = getScrollDirections(
endPointRef.current, endPointRef.current,
scrollManager.verticalScrollTriggerDistance, scrollManager.verticalScrollTriggerDistance,
@ -204,6 +203,7 @@ export const SelectionRect = forwardRef<SelectionRef, SelectionProps>(
mouseType.current = 'up'; mouseType.current = 'up';
startPointBlock.current = null; startPointBlock.current = null;
setShow(false); setShow(false);
setRect(Rect.fromLTRB(0, 0, 0, 0));
scrollManager.stopAutoScroll(); scrollManager.stopAutoScroll();
}; };

View File

@ -159,6 +159,33 @@ export class BlockCommands {
return []; 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) { public async splitGroupFromBlock(blockId: string) {
const block = await this._editor.getBlockById(blockId); const block = await this._editor.getBlockById(blockId);
await splitGroup(this._editor, block); await splitGroup(this._editor, block);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import { domToRect, Point } 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';
@ -5,6 +6,7 @@ import { Editor } from '../editor';
import { BlockDropPlacement, GroupDirection } from '../types'; 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';
import { Protocol } from '@toeverything/datasource/db-service';
enum DragType { enum DragType {
dragBlock = 'dragBlock', dragBlock = 'dragBlock',
@ -86,6 +88,7 @@ export class DragDropManager {
while (curr !== this._editor.getRootBlockId()) { while (curr !== this._editor.getRootBlockId()) {
if (curr === blockId) return false; if (curr === blockId) return false;
const block = await this._editor.getBlockById(curr); const block = await this._editor.getBlockById(curr);
if (!block) return false;
curr = block.parentId; curr = block.parentId;
} }
return true; return true;
@ -114,6 +117,48 @@ export class DragDropManager {
: GridDropType.right : 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<Element>} 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<Element>) {
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( public async checkBlockDragTypes(
event: React.DragEvent<Element>, event: React.DragEvent<Element>,
blockDom: HTMLElement, blockDom: HTMLElement,
@ -216,10 +348,25 @@ export class DragDropManager {
) { ) {
const { clientX, clientY } = event; const { clientX, clientY } = event;
this._setBlockDragTargetId(blockId); this._setBlockDragTargetId(blockId);
const path = await this._editor.getBlockPath(blockId);
const mousePoint = new Point(clientX, clientY); const mousePoint = new Point(clientX, clientY);
const rect = domToRect(blockDom); 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; let direction = BlockDropPlacement.bottom;
if (mousePoint.x - rect.left <= this._dragBlockHotDistance) { if (mousePoint.x - rect.left <= this._dragBlockHotDistance) {
direction = BlockDropPlacement.left; direction = BlockDropPlacement.left;
} }
@ -236,9 +383,10 @@ export class DragDropManager {
direction === BlockDropPlacement.left || direction === BlockDropPlacement.left ||
direction === BlockDropPlacement.right direction === BlockDropPlacement.right
) { ) {
const path = await this._editor.getBlockPath(blockId); const gridBlocks = path.filter(
const gridBlocks = path.filter(block => block.type === 'grid'); block => block.type === Protocol.Block.Type.grid
// limit grid block floor counts );
// limit grid block floor counts, when drag block to init grid
if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) { if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) {
direction = BlockDropPlacement.none; direction = BlockDropPlacement.none;
} }

View File

@ -3,6 +3,8 @@ export enum BlockDropPlacement {
right = 'right', right = 'right',
top = 'top', top = 'top',
bottom = 'bottom', bottom = 'bottom',
outerLeft = 'outer-left',
outerRight = 'outer-right',
none = 'none', none = 'none',
} }

View File

@ -35,6 +35,7 @@ import { BrowserClipboard } from './clipboard/browser-clipboard';
import { ClipboardPopulator } from './clipboard/clipboard-populator'; import { ClipboardPopulator } from './clipboard/clipboard-populator';
import { BlockHelper } from './block/block-helper'; import { BlockHelper } from './block/block-helper';
import { DragDropManager } from './drag-drop'; import { DragDropManager } from './drag-drop';
import { EditorConfig } from './config';
export interface EditorCtorProps { export interface EditorCtorProps {
workspace: string; workspace: string;
@ -56,6 +57,7 @@ export class Editor implements Virgo {
public dragDropManager = new DragDropManager(this); public dragDropManager = new DragDropManager(this);
public commands = new EditorCommands(this); public commands = new EditorCommands(this);
public blockHelper = new BlockHelper(this); public blockHelper = new BlockHelper(this);
public configManager = new EditorConfig(this);
public bdCommands: Commands; public bdCommands: Commands;
public ui_container?: HTMLDivElement; public ui_container?: HTMLDivElement;
public version = '0.0.1'; public version = '0.0.1';
@ -343,6 +345,23 @@ export class Editor implements Virgo {
return [...blockList, ...(await this.getOffspring(rootBlockId))]; return [...blockList, ...(await this.getOffspring(rootBlockId))];
} }
async getBlockListByLevelOrder() {
const rootBlockId = this.getRootBlockId();
const rootBlock = await this.getBlockById(rootBlockId);
const blockList: Array<AsyncBlock> = [];
let nextToVisit: Array<AsyncBlock> = rootBlock ? [rootBlock] : [];
while (nextToVisit.length) {
let next: Array<AsyncBlock> = [];
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 * get all offspring of block

View File

@ -110,7 +110,6 @@ export class ScrollManager {
} }
public emitScrollEvent(event: UIEvent) { public emitScrollEvent(event: UIEvent) {
this.scrollContainer = event.target as HTMLElement;
this._scrollDirection = this._getScrollDirection(); this._scrollDirection = this._getScrollDirection();
this._scrollMoveOffset = Math.abs( this._scrollMoveOffset = Math.abs(
this.scrollContainer.scrollTop - this._scrollRecord[0] this.scrollContainer.scrollTop - this._scrollRecord[0]

View File

@ -73,6 +73,7 @@ export interface Virgo {
getBlockById(blockId: string): Promise<AsyncBlock | null>; getBlockById(blockId: string): Promise<AsyncBlock | null>;
setHotKeysScope(scope?: string): void; setHotKeysScope(scope?: string): void;
getBlockList: () => Promise<AsyncBlock[]>; getBlockList: () => Promise<AsyncBlock[]>;
getBlockListByLevelOrder: () => Promise<AsyncBlock[]>;
// removeBlocks: () => void; // removeBlocks: () => void;
storageManager: StorageManager | undefined; storageManager: StorageManager | undefined;
selection: VirgoSelection; selection: VirgoSelection;

View File

@ -52,7 +52,6 @@ function Line(props: { lineInfo: LineInfo; rootRect: DOMRect }) {
return null; return null;
} }
const { direction, blockInfo } = lineInfo; const { direction, blockInfo } = lineInfo;
const finalDirection = direction;
const lineStyle = { const lineStyle = {
zIndex: 2, zIndex: 2,
position: 'absolute' as const, position: 'absolute' as const,
@ -91,14 +90,14 @@ function Line(props: { lineInfo: LineInfo; rootRect: DOMRect }) {
left: intersectionRect.right + 10 - rootRect.x, left: intersectionRect.right + 10 - rootRect.x,
}; };
const styleMap = { const styleMap = {
left: leftLineStyle, [BlockDropPlacement.left]: leftLineStyle,
right: rightLineStyle, [BlockDropPlacement.right]: rightLineStyle,
top: topLineStyle, [BlockDropPlacement.top]: topLineStyle,
bottom: bottomLineStyle, [BlockDropPlacement.bottom]: bottomLineStyle,
[BlockDropPlacement.outerLeft]: leftLineStyle,
[BlockDropPlacement.outerRight]: rightLineStyle,
}; };
return ( return <div className="editor-menu-line" style={styleMap[direction]} />;
<div className="editor-menu-line" style={styleMap[finalDirection]} />
);
} }
function DragComponent(props: { function DragComponent(props: {

View File

@ -5,8 +5,9 @@ import { ignoreBlockTypes } from './menu-config';
import { LineInfoSubject, LeftMenuDraggable } from './LeftMenuDraggable'; import { LineInfoSubject, LeftMenuDraggable } from './LeftMenuDraggable';
import { PluginRenderRoot } from '../../utils'; import { PluginRenderRoot } from '../../utils';
import { Subject } from 'rxjs'; 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 { export class LeftMenuPlugin extends BasePlugin {
private _mousedown?: boolean; private _mousedown?: boolean;
private _root?: PluginRenderRoot; private _root?: PluginRenderRoot;
@ -35,11 +36,7 @@ export class LeftMenuPlugin extends BasePlugin {
.get(HookType.ON_ROOTNODE_MOUSE_UP) .get(HookType.ON_ROOTNODE_MOUSE_UP)
.subscribe(this._handleMouseUp) .subscribe(this._handleMouseUp)
); );
this.sub.add(
this.hooks
.get(HookType.ON_ROOTNODE_DRAG_OVER)
.subscribe(this._handleDragOverBlockNode)
);
this.sub.add( this.sub.add(
this.hooks.get(HookType.ON_ROOTNODE_MOUSE_LEAVE).subscribe(() => { this.hooks.get(HookType.ON_ROOTNODE_MOUSE_LEAVE).subscribe(() => {
this._hideLeftMenu(); this._hideLeftMenu();
@ -60,8 +57,47 @@ export class LeftMenuPlugin extends BasePlugin {
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)
); );
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<Element>
) => {
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 = () => { private _onDrop = () => {
this._lineInfo.next(undefined); this._lineInfo.next(undefined);
}; };

View File

@ -121,20 +121,20 @@ export class Rect {
} }
} }
isPointDown({ y }: Point) { isPointDown({ x, y }: Point) {
return this.bottom < y; return this.bottom < y && this.left <= x && this.right >= x;
} }
isPointUp({ y }: Point) { isPointUp({ x, y }: Point) {
return y < this.top; return y < this.top && this.left <= x && this.right >= x;
} }
isPointLeft({ x }: Point) { isPointLeft({ x, y }: Point) {
return x < this.left; return x < this.left && this.top <= y && this.bottom >= y;
} }
isPointRight({ x }: Point) { isPointRight({ x, y }: Point) {
return x > this.right; return x > this.right && this.top <= y && this.bottom >= y;
} }
fromNewLeft(left: number) { fromNewLeft(left: number) {