mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
feat: outer drag 、fix new page selection
This commit is contained in:
@@ -187,7 +187,6 @@ export const SelectionRect = forwardRef<SelectionRef, SelectionProps>(
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const scrollDirections = getScrollDirections(
|
||||
endPointRef.current,
|
||||
scrollManager.verticalScrollTriggerDistance,
|
||||
@@ -204,6 +203,7 @@ export const SelectionRect = forwardRef<SelectionRef, SelectionProps>(
|
||||
mouseType.current = 'up';
|
||||
startPointBlock.current = null;
|
||||
setShow(false);
|
||||
setRect(Rect.fromLTRB(0, 0, 0, 0));
|
||||
scrollManager.stopAutoScroll();
|
||||
};
|
||||
|
||||
|
||||
@@ -159,6 +159,33 @@ export class BlockCommands {
|
||||
return [];
|
||||
}
|
||||
|
||||
public async moveInNewGridItem(
|
||||
blockId: string,
|
||||
gridItemId: string,
|
||||
isBefore = false
|
||||
) {
|
||||
const block = await this._editor.getBlockById(blockId);
|
||||
if (block) {
|
||||
const gridItemBlock = await this._editor.createBlock(
|
||||
Protocol.Block.Type.gridItem
|
||||
);
|
||||
const targetGridItemBlock = await this._editor.getBlockById(
|
||||
gridItemId
|
||||
);
|
||||
await block.remove();
|
||||
await gridItemBlock.append(block);
|
||||
if (targetGridItemBlock && gridItemBlock) {
|
||||
if (isBefore) {
|
||||
await targetGridItemBlock.before(gridItemBlock);
|
||||
} else {
|
||||
await targetGridItemBlock.after(gridItemBlock);
|
||||
}
|
||||
}
|
||||
return gridItemBlock;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async splitGroupFromBlock(blockId: string) {
|
||||
const block = await this._editor.getBlockById(blockId);
|
||||
await splitGroup(this._editor, block);
|
||||
|
||||
27
libs/components/editor-core/src/editor/config/grid.ts
Normal file
27
libs/components/editor-core/src/editor/config/grid.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
23
libs/components/editor-core/src/editor/config/index.ts
Normal file
23
libs/components/editor-core/src/editor/config/index.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { domToRect, Point } from '@toeverything/utils';
|
||||
import { AsyncBlock } from '../..';
|
||||
import { GridDropType } from '../commands/types';
|
||||
@@ -5,6 +6,7 @@ import { Editor } from '../editor';
|
||||
import { BlockDropPlacement, GroupDirection } from '../types';
|
||||
// TODO: Evaluate implementing custom events with Rxjs
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Protocol } from '@toeverything/datasource/db-service';
|
||||
|
||||
enum DragType {
|
||||
dragBlock = 'dragBlock',
|
||||
@@ -86,6 +88,7 @@ export class DragDropManager {
|
||||
while (curr !== this._editor.getRootBlockId()) {
|
||||
if (curr === blockId) return false;
|
||||
const block = await this._editor.getBlockById(curr);
|
||||
if (!block) return false;
|
||||
curr = block.parentId;
|
||||
}
|
||||
return true;
|
||||
@@ -114,6 +117,48 @@ export class DragDropManager {
|
||||
: GridDropType.right
|
||||
);
|
||||
}
|
||||
if (
|
||||
[
|
||||
BlockDropPlacement.outerLeft,
|
||||
BlockDropPlacement.outerRight,
|
||||
].includes(this._blockDragDirection)
|
||||
) {
|
||||
const targetBlock = await this._editor.getBlockById(
|
||||
this._blockDragTargetId
|
||||
);
|
||||
if (targetBlock.type !== Protocol.Block.Type.grid) {
|
||||
await this._editor.commands.blockCommands.createLayoutBlock(
|
||||
blockId,
|
||||
this._blockDragTargetId,
|
||||
this._blockDragDirection ===
|
||||
BlockDropPlacement.outerLeft
|
||||
? GridDropType.left
|
||||
: GridDropType.right
|
||||
);
|
||||
}
|
||||
if (targetBlock.type === Protocol.Block.Type.grid) {
|
||||
const gridItems = await targetBlock.children();
|
||||
if (
|
||||
BlockDropPlacement.outerRight ===
|
||||
this._blockDragDirection
|
||||
) {
|
||||
await this._editor.commands.blockCommands.moveInNewGridItem(
|
||||
blockId,
|
||||
gridItems[gridItems.length - 1].id
|
||||
);
|
||||
}
|
||||
if (
|
||||
BlockDropPlacement.outerLeft ===
|
||||
this._blockDragDirection
|
||||
) {
|
||||
await this._editor.commands.blockCommands.moveInNewGridItem(
|
||||
blockId,
|
||||
gridItems[0].id,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +254,93 @@ export class DragDropManager {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* check if drag block is out of blocks and return direction
|
||||
* @param {React.DragEvent<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(
|
||||
event: React.DragEvent<Element>,
|
||||
blockDom: HTMLElement,
|
||||
@@ -216,10 +348,25 @@ export class DragDropManager {
|
||||
) {
|
||||
const { clientX, clientY } = event;
|
||||
this._setBlockDragTargetId(blockId);
|
||||
|
||||
const path = await this._editor.getBlockPath(blockId);
|
||||
const mousePoint = new Point(clientX, clientY);
|
||||
const rect = domToRect(blockDom);
|
||||
/**
|
||||
* IMP: compute the level of the target block
|
||||
* future feature drag drop has level support do not delete
|
||||
* const levelUnderGrid = Array.from(path)
|
||||
.reverse()
|
||||
.findIndex(block => block.type === Protocol.Block.Type.gridItem);
|
||||
const levelUnderGroup = Array.from(path)
|
||||
.reverse()
|
||||
.findIndex(block => block.type === Protocol.Block.Type.group);
|
||||
const blockLevel =
|
||||
levelUnderGrid > 0 ? levelUnderGrid : levelUnderGroup;
|
||||
console.log({ blockLevel, levelUnderGrid, levelUnderGroup });
|
||||
*
|
||||
*/
|
||||
let direction = BlockDropPlacement.bottom;
|
||||
|
||||
if (mousePoint.x - rect.left <= this._dragBlockHotDistance) {
|
||||
direction = BlockDropPlacement.left;
|
||||
}
|
||||
@@ -236,9 +383,10 @@ export class DragDropManager {
|
||||
direction === BlockDropPlacement.left ||
|
||||
direction === BlockDropPlacement.right
|
||||
) {
|
||||
const path = await this._editor.getBlockPath(blockId);
|
||||
const gridBlocks = path.filter(block => block.type === 'grid');
|
||||
// limit grid block floor counts
|
||||
const gridBlocks = path.filter(
|
||||
block => block.type === Protocol.Block.Type.grid
|
||||
);
|
||||
// limit grid block floor counts, when drag block to init grid
|
||||
if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) {
|
||||
direction = BlockDropPlacement.none;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ export enum BlockDropPlacement {
|
||||
right = 'right',
|
||||
top = 'top',
|
||||
bottom = 'bottom',
|
||||
outerLeft = 'outer-left',
|
||||
outerRight = 'outer-right',
|
||||
none = 'none',
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import { BrowserClipboard } from './clipboard/browser-clipboard';
|
||||
import { ClipboardPopulator } from './clipboard/clipboard-populator';
|
||||
import { BlockHelper } from './block/block-helper';
|
||||
import { DragDropManager } from './drag-drop';
|
||||
import { EditorConfig } from './config';
|
||||
|
||||
export interface EditorCtorProps {
|
||||
workspace: string;
|
||||
@@ -56,6 +57,7 @@ export class Editor implements Virgo {
|
||||
public dragDropManager = new DragDropManager(this);
|
||||
public commands = new EditorCommands(this);
|
||||
public blockHelper = new BlockHelper(this);
|
||||
public configManager = new EditorConfig(this);
|
||||
public bdCommands: Commands;
|
||||
public ui_container?: HTMLDivElement;
|
||||
public version = '0.0.1';
|
||||
@@ -343,6 +345,23 @@ export class Editor implements Virgo {
|
||||
return [...blockList, ...(await this.getOffspring(rootBlockId))];
|
||||
}
|
||||
|
||||
async getBlockListByLevelOrder() {
|
||||
const rootBlockId = this.getRootBlockId();
|
||||
const rootBlock = await this.getBlockById(rootBlockId);
|
||||
const blockList: Array<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
|
||||
|
||||
@@ -110,7 +110,6 @@ export class ScrollManager {
|
||||
}
|
||||
|
||||
public emitScrollEvent(event: UIEvent) {
|
||||
this.scrollContainer = event.target as HTMLElement;
|
||||
this._scrollDirection = this._getScrollDirection();
|
||||
this._scrollMoveOffset = Math.abs(
|
||||
this.scrollContainer.scrollTop - this._scrollRecord[0]
|
||||
|
||||
@@ -73,6 +73,7 @@ export interface Virgo {
|
||||
getBlockById(blockId: string): Promise<AsyncBlock | null>;
|
||||
setHotKeysScope(scope?: string): void;
|
||||
getBlockList: () => Promise<AsyncBlock[]>;
|
||||
getBlockListByLevelOrder: () => Promise<AsyncBlock[]>;
|
||||
// removeBlocks: () => void;
|
||||
storageManager: StorageManager | undefined;
|
||||
selection: VirgoSelection;
|
||||
|
||||
Reference in New Issue
Block a user