From 0b3dba614db8ea5c358ad467ecd26844a60a3f06 Mon Sep 17 00:00:00 2001 From: fundon Date: Fri, 10 Jan 2025 08:21:10 +0000 Subject: [PATCH] perf(editor): optimize the search for the closest element (#9587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes: [BS-2275](https://linear.app/affine-design/issue/BS-2275/拖拽-indicator-查找优化) --- .../src/attachment-service.ts | 5 +- .../block-database/src/database-block.ts | 4 +- .../affine/block-image/src/image-service.ts | 4 +- .../src/drag-indicator/file-drop-manager.ts | 276 ++++++++++-------- .../components/src/drag-indicator/index.ts | 6 +- .../shared/src/utils/dnd/calc-drop-target.ts | 34 +-- .../affine/shared/src/utils/dnd/types.ts | 8 +- .../shared/src/utils/dom/point-to-block.ts | 11 +- .../widget-drag-handle/src/drag-handle.ts | 38 +-- .../affine/widget-drag-handle/src/utils.ts | 4 +- .../src/watchers/drag-event-watcher.ts | 8 +- 11 files changed, 211 insertions(+), 187 deletions(-) diff --git a/blocksuite/affine/block-attachment/src/attachment-service.ts b/blocksuite/affine/block-attachment/src/attachment-service.ts index 71c806dc8..14e38cd42 100644 --- a/blocksuite/affine/block-attachment/src/attachment-service.ts +++ b/blocksuite/affine/block-attachment/src/attachment-service.ts @@ -21,12 +21,11 @@ export class AttachmentBlockService extends BlockService { export const AttachmentDropOption = FileDropConfigExtension({ flavour: AttachmentBlockSchema.model.flavour, - onDrop: ({ files, targetModel, place, point, std }) => { + onDrop: ({ files, targetModel, placement, point, std }) => { // generic attachment block for all files except images const attachmentFiles = files.filter( file => !file.type.startsWith('image/') ); - if (!attachmentFiles.length) return false; if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) { @@ -36,7 +35,7 @@ export const AttachmentDropOption = FileDropConfigExtension({ // TODO: use max file size from service maxFileSize, targetModel, - place + placement ).catch(console.error); return true; diff --git a/blocksuite/affine/block-database/src/database-block.ts b/blocksuite/affine/block-database/src/database-block.ts index f0a5f5745..46e94693d 100644 --- a/blocksuite/affine/block-database/src/database-block.ts +++ b/blocksuite/affine/block-database/src/database-block.ts @@ -262,7 +262,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent< const model = this.doc.getBlock(id)?.model; const target = result.modelState.model; let parent = this.doc.getParent(target.id); - const shouldInsertIn = result.type === 'in'; + const shouldInsertIn = result.placement === 'in'; if (shouldInsertIn) { parent = target; } @@ -274,7 +274,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent< [model], parent, target, - result.type === 'before' + result.placement === 'before' ); } } diff --git a/blocksuite/affine/block-image/src/image-service.ts b/blocksuite/affine/block-image/src/image-service.ts index 92531de7e..ab49bba67 100644 --- a/blocksuite/affine/block-image/src/image-service.ts +++ b/blocksuite/affine/block-image/src/image-service.ts @@ -24,7 +24,7 @@ export class ImageBlockService extends BlockService { export const ImageDropOption = FileDropConfigExtension({ flavour: ImageBlockSchema.model.flavour, - onDrop: ({ files, targetModel, place, point, std }) => { + onDrop: ({ files, targetModel, placement, point, std }) => { const imageFiles = files.filter(file => file.type.startsWith('image/')); if (!imageFiles.length) return false; @@ -35,7 +35,7 @@ export const ImageDropOption = FileDropConfigExtension({ // TODO: use max file size from service maxFileSize, targetModel, - place + placement ); return true; } diff --git a/blocksuite/affine/components/src/drag-indicator/file-drop-manager.ts b/blocksuite/affine/components/src/drag-indicator/file-drop-manager.ts index f3c34b660..69c177c0f 100644 --- a/blocksuite/affine/components/src/drag-indicator/file-drop-manager.ts +++ b/blocksuite/affine/components/src/drag-indicator/file-drop-manager.ts @@ -1,40 +1,44 @@ import { calcDropTarget, - type DropResult, + type DropTarget, getClosestBlockComponentByPoint, isInsidePageEditor, matchFlavours, } from '@blocksuite/affine-shared/utils'; import { + type BlockComponent, type BlockStdScope, type EditorHost, LifeCycleWatcher, } from '@blocksuite/block-std'; import { createIdentifier } from '@blocksuite/global/di'; import type { IVec } from '@blocksuite/global/utils'; -import { Point } from '@blocksuite/global/utils'; +import { Point, throttle } from '@blocksuite/global/utils'; import type { BlockModel, ExtensionType } from '@blocksuite/store'; +import { computed, signal } from '@preact/signals-core'; -import type { DragIndicator } from './index.js'; +import type { DragIndicator } from './drag-indicator'; -export type onDropProps = { +export type DropProps = { std: BlockStdScope; files: File[]; targetModel: BlockModel | null; - place: 'before' | 'after'; + placement: 'before' | 'after'; point: IVec; }; export type FileDropOptions = { flavour: string; - onDrop?: (onDropProps: onDropProps) => boolean; + onDrop?: (props: DropProps) => boolean; }; +/** + * Handles resources from outside. + * Uses `drag over` to handle it. + */ export class FileDropExtension extends LifeCycleWatcher { static override readonly key = 'FileDropExtension'; - static dropResult: DropResult | null = null; - static get indicator() { let indicator = document.querySelector( 'affine-drag-indicator' @@ -50,73 +54,103 @@ export class FileDropExtension extends LifeCycleWatcher { return indicator; } - onDragLeave = () => { - FileDropExtension.dropResult = null; - FileDropExtension.indicator.rect = null; - }; + point$ = signal(new Point(0, 0)); - onDragMove = (event: DragEvent) => { - event.preventDefault(); + closestElement$ = signal(null); + dropTarget$ = computed(() => { + let target = null; + const element = this.closestElement$.value; + if (!element) return null; + + const model = element.model; + const parent = this.std.store.getParent(model); + if (!matchFlavours(parent, ['affine:surface' as BlockSuite.Flavour])) { + const point = this.point$.value; + target = calcDropTarget(point, model, element); + } + + return target; + }); + + getDropTargetModel(model: BlockModel | null) { + // Existed or In Edgeless + if (model || !isInsidePageEditor(this.editorHost)) return model; + + const rootModel = this.doc.root; + if (!rootModel) return null; + + let lastNote = rootModel.children[rootModel.children.length - 1]; + if (!lastNote || !matchFlavours(lastNote, ['affine:note'])) { + const newNoteId = this.doc.addBlock('affine:note', {}, rootModel.id); + const newNote = this.doc.getBlock(newNoteId)?.model; + if (!newNote) return null; + lastNote = newNote; + } + + const lastItem = lastNote.children[lastNote.children.length - 1]; + if (lastItem) { + model = lastItem; + } else { + const newParagraphId = this.doc.addBlock( + 'affine:paragraph', + {}, + lastNote, + 0 + ); + model = this.doc.getBlock(newParagraphId)?.model ?? null; + } + + return model; + } + + shouldIgnoreEvent = (event: DragEvent, shouldCheckFiles?: boolean) => { const dataTransfer = event.dataTransfer; - if (!dataTransfer) return; + if (!dataTransfer) return true; const effectAllowed = dataTransfer.effectAllowed; - if (effectAllowed === 'none') return; + if (effectAllowed === 'none') return true; - const { clientX, clientY } = event; - const point = new Point(clientX, clientY); - const element = getClosestBlockComponentByPoint(point.clone()); + if (!shouldCheckFiles) return false; - let result: DropResult | null = null; - if (element) { - const model = element.model; - const parent = this.std.store.getParent(model); - if (!matchFlavours(parent, ['affine:surface' as BlockSuite.Flavour])) { - result = calcDropTarget(point, model, element); - } - } - if (result) { - FileDropExtension.dropResult = result; - FileDropExtension.indicator.rect = result.rect; - } else { - FileDropExtension.dropResult = null; - FileDropExtension.indicator.rect = null; - } + const droppedFiles = dataTransfer.files; + if (!droppedFiles || !droppedFiles.length) return true; + + return false; }; - get targetModel(): BlockModel | null { - let targetModel = FileDropExtension.dropResult?.modelState.model || null; + updatePoint = (event: DragEvent) => { + const { clientX, clientY } = event; + const oldPoint = this.point$.peek(); - if (!targetModel && isInsidePageEditor(this.editorHost)) { - const rootModel = this.doc.root; - if (!rootModel) return null; + if ( + Math.round(oldPoint.x) === Math.round(clientX) && + Math.round(oldPoint.y) === Math.round(clientY) + ) + return; - let lastNote = rootModel.children[rootModel.children.length - 1]; - if (!lastNote || !matchFlavours(lastNote, ['affine:note'])) { - const newNoteId = this.doc.addBlock('affine:note', {}, rootModel.id); - const newNote = this.doc.getBlockById(newNoteId); - if (!newNote) return null; - lastNote = newNote; - } + this.point$.value = new Point(clientX, clientY); + }; - const lastItem = lastNote.children[lastNote.children.length - 1]; - if (lastItem) { - targetModel = lastItem; - } else { - const newParagraphId = this.doc.addBlock( - 'affine:paragraph', - {}, - lastNote, - 0 - ); - const newParagraph = this.doc.getBlockById(newParagraphId); - if (!newParagraph) return null; - targetModel = newParagraph; - } - } - return targetModel; - } + onDragLeave = () => { + this.closestElement$.value = null; + }; + + onDragOver = (event: DragEvent) => { + event.preventDefault(); + + if (this.shouldIgnoreEvent(event)) return; + + this.updatePoint(event); + }; + + onDrop = (event: DragEvent) => { + event.preventDefault(); + + if (this.shouldIgnoreEvent(event, true)) return; + + this.updatePoint(event); + }; get doc() { return this.std.store; @@ -126,66 +160,34 @@ export class FileDropExtension extends LifeCycleWatcher { return this.std.host; } - get type(): 'before' | 'after' { - return !FileDropExtension.dropResult || - FileDropExtension.dropResult.type !== 'before' - ? 'after' - : 'before'; - } - - private readonly _onDrop = (event: DragEvent, options: FileDropOptions) => { - FileDropExtension.indicator.rect = null; - - const { onDrop } = options; - if (!onDrop) return; - - const dataTransfer = event.dataTransfer; - if (!dataTransfer) return; - - const effectAllowed = dataTransfer.effectAllowed; - if (effectAllowed === 'none') return; - - const droppedFiles = dataTransfer.files; - if (!droppedFiles || !droppedFiles.length) return; - - const { clientX, clientY } = event; - const point = new Point(clientX, clientY); - const element = getClosestBlockComponentByPoint(point.clone()); - - let result: DropResult | null = null; - if (element) { - const model = element.model; - const parent = this.std.store.getParent(model); - if (!matchFlavours(parent, ['affine:surface' as BlockSuite.Flavour])) { - result = calcDropTarget(point, model, element); - } - } - FileDropExtension.dropResult = result; - - const { x, y } = event; - const { targetModel, type: place } = this; - const drop = onDrop({ - std: this.std, - files: [...droppedFiles], - targetModel, - place, - point: [x, y], - }); - - if (drop) { - event.preventDefault(); - } - return drop; - }; - override mounted() { super.mounted(); const std = this.std; std.event.disposables.add( - std.event.add('nativeDragMove', context => { + this.point$.subscribe( + throttle( + value => { + if (value.x * value.y === 0) return; + + this.closestElement$.value = getClosestBlockComponentByPoint(value); + }, + 233, + { leading: true, trailing: true } + ) + ) + ); + + std.event.disposables.add( + this.dropTarget$.subscribe(target => { + FileDropExtension.indicator.rect = target?.rect ?? null; + }) + ); + + std.event.disposables.add( + std.event.add('nativeDragOver', context => { const event = context.get('dndState'); - this.onDragMove(event.raw); + this.onDragOver(event.raw); }) ); std.event.disposables.add( @@ -195,19 +197,43 @@ export class FileDropExtension extends LifeCycleWatcher { ); std.event.disposables.add( std.event.add('nativeDrop', context => { + const event = context.get('dndState').raw; + const { x, y, dataTransfer } = event; + const droppedFiles = dataTransfer?.files; + + if (!droppedFiles || !droppedFiles.length) { + this.onDragLeave(); + return; + } + + this.onDrop(event); + + const target = this.dropTarget$.peek(); + const std = this.std; + const targetModel = this.getDropTargetModel( + target?.modelState.model ?? null + ); + const placement = target?.placement === 'before' ? 'before' : 'after'; + const values = std.provider .getAll(FileDropConfigExtensionIdentifier) .values(); - for (const value of values) { - if (value.onDrop) { - const event = context.get('dndState'); - const drop = this._onDrop(event.raw, value); - if (drop) { - return; - } - } + for (const ext of values) { + if (!ext.onDrop) continue; + + const options = { + std, + files: [...droppedFiles], + targetModel, + placement, + point: [x, y], + } satisfies DropProps; + + if (ext.onDrop(options)) break; } + + this.onDragLeave(); }) ); } diff --git a/blocksuite/affine/components/src/drag-indicator/index.ts b/blocksuite/affine/components/src/drag-indicator/index.ts index db2f29909..cc731a2ea 100644 --- a/blocksuite/affine/components/src/drag-indicator/index.ts +++ b/blocksuite/affine/components/src/drag-indicator/index.ts @@ -1,10 +1,10 @@ -import { DragIndicator } from './drag-indicator.js'; +import { DragIndicator } from './drag-indicator'; export { + type DropProps, FileDropConfigExtension, FileDropExtension, type FileDropOptions, - type onDropProps, -} from './file-drop-manager.js'; +} from './file-drop-manager'; export { DragIndicator }; diff --git a/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts b/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts index 65729730c..91594b112 100644 --- a/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts +++ b/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts @@ -9,7 +9,7 @@ import { } from '../dom/index.js'; import { matchFlavours } from '../model/index.js'; import { getDropRectByPoint } from './get-drop-rect-by-point.js'; -import { DropFlags, type DroppingType, type DropResult } from './types.js'; +import { DropFlags, type DropPlacement, type DropTarget } from './types.js'; function getVisiblePreviousElementSibling(element: Element | null) { if (!element) return null; @@ -43,7 +43,7 @@ export function calcDropTarget( * Allow the dragging block to be dropped as sublist */ allowSublist: boolean = true -): DropResult | null { +): DropTarget | null { const schema = model.doc.getSchemaByFlavour('affine:database'); const children = schema?.model.children ?? []; @@ -64,7 +64,7 @@ export function calcDropTarget( } } - let type: DroppingType = 'none'; + let placement: DropPlacement = 'none'; const height = 3 * scale; const dropResult = getDropRectByPoint(point, model, element); if (!dropResult) return null; @@ -75,10 +75,10 @@ export function calcDropTarget( const rect = Rect.fromDOMRect(domRect); rect.top -= height / 2; rect.height = height; - type = 'database'; + placement = 'database'; return { - type, + placement, rect, modelState: { model, @@ -91,10 +91,10 @@ export function calcDropTarget( const distanceToTop = Math.abs(domRect.top - point.y); const distanceToBottom = Math.abs(domRect.bottom - point.y); const before = distanceToTop < distanceToBottom; - type = before ? 'before' : 'after'; + placement = before ? 'before' : 'after'; return { - type, + placement, rect: Rect.fromLWTH( domRect.left, domRect.width, @@ -113,10 +113,10 @@ export function calcDropTarget( const distanceToBottom = Math.abs(domRect.bottom - point.y); const before = distanceToTop < distanceToBottom; - type = before ? 'before' : 'after'; + placement = before ? 'before' : 'after'; let offsetY = 4; - if (type === 'before') { + if (placement === 'before') { // before let prev; let prevRect; @@ -127,7 +127,7 @@ export function calcDropTarget( draggingElements.length && prev === draggingElements[draggingElements.length - 1] ) { - type = 'none'; + placement = 'none'; } else { prevRect = getRectByBlockComponent(prev); } @@ -153,7 +153,7 @@ export function calcDropTarget( !hasChild && point.x > domRect.x + BLOCK_CHILDREN_CONTAINER_PADDING_LEFT ) { - type = 'in'; + placement = 'in'; } // after let next; @@ -162,11 +162,11 @@ export function calcDropTarget( next = getVisibleNextElementSibling(element); if (next) { if ( - type === 'after' && + placement === 'after' && draggingElements.length && next === draggingElements[0] ) { - type = 'none'; + placement = 'none'; next = null; } } else { @@ -181,22 +181,22 @@ export function calcDropTarget( } } - if (type === 'none') return null; + if (placement === 'none') return null; let top = domRect.top; - if (type === 'before') { + if (placement === 'before') { top -= offsetY; } else { top += domRect.height + offsetY; } - if (type === 'in') { + if (placement === 'in') { domRect.x += BLOCK_CHILDREN_CONTAINER_PADDING_LEFT; domRect.width -= BLOCK_CHILDREN_CONTAINER_PADDING_LEFT; } return { - type, + placement, rect: Rect.fromLWTH(domRect.left, domRect.width, top - height / 2, height), modelState: { model, diff --git a/blocksuite/affine/shared/src/utils/dnd/types.ts b/blocksuite/affine/shared/src/utils/dnd/types.ts index 8fa69a3c6..c4fa1e697 100644 --- a/blocksuite/affine/shared/src/utils/dnd/types.ts +++ b/blocksuite/affine/shared/src/utils/dnd/types.ts @@ -18,12 +18,12 @@ export enum DropFlags { } /** - * A dropping type. + * A drop placement. */ -export type DroppingType = 'none' | 'before' | 'after' | 'database' | 'in'; +export type DropPlacement = 'none' | 'before' | 'after' | 'database' | 'in'; -export type DropResult = { - type: DroppingType; +export type DropTarget = { + placement: DropPlacement; rect: Rect; modelState: EditingState; }; diff --git a/blocksuite/affine/shared/src/utils/dom/point-to-block.ts b/blocksuite/affine/shared/src/utils/dom/point-to-block.ts index cea1e8041..6add25bf9 100644 --- a/blocksuite/affine/shared/src/utils/dom/point-to-block.ts +++ b/blocksuite/affine/shared/src/utils/dom/point-to-block.ts @@ -146,8 +146,8 @@ export function getClosestBlockComponentByPoint( } } } else { - // Indented paragraphs or list bounds = getRectByBlockComponent(element); + // Indented paragraphs or list childBounds = element .querySelector('.affine-block-children-container') ?.firstElementChild?.getBoundingClientRect(); @@ -263,8 +263,8 @@ export function getClosestBlockComponentByElement( * https://github.com/toeverything/blocksuite/pull/1121 */ export function getRectByBlockComponent(element: Element | BlockComponent) { - if (isDatabase(element)) return element.getBoundingClientRect(); - return (element.firstElementChild ?? element).getBoundingClientRect(); + if (!isDatabase(element)) element = element.firstElementChild ?? element; + return element.getBoundingClientRect(); } /** @@ -303,9 +303,8 @@ function findBlockComponent(elements: Element[], parent?: Element) { if (hasBlockId(element) && isBlock(element)) return element; if (isImage(element)) { const element = elements[i]; - if (i < len && hasBlockId(element) && isBlock(element)) { - return elements[i]; - } + if (!element) return null; + if (hasBlockId(element) && isBlock(element)) return element; return getClosestBlockComponentByElement(element); } } diff --git a/blocksuite/affine/widget-drag-handle/src/drag-handle.ts b/blocksuite/affine/widget-drag-handle/src/drag-handle.ts index 1dfd427cf..7c3273292 100644 --- a/blocksuite/affine/widget-drag-handle/src/drag-handle.ts +++ b/blocksuite/affine/widget-drag-handle/src/drag-handle.ts @@ -4,8 +4,8 @@ import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { autoScroll, calcDropTarget, - type DroppingType, - type DropResult, + type DropPlacement, + type DropTarget, getScrollContainer, isInsideEdgelessEditor, isInsidePageEditor, @@ -61,9 +61,9 @@ export class AffineDragHandleWidget extends WidgetComponent { /** * When dragging, should update indicator position and target drop block id */ - private readonly _getDropResult = ( + private readonly _getDropTarget = ( state: DndEventState - ): DropResult | null => { + ): DropTarget | null => { const point = new Point(state.raw.x, state.raw.y); const closestBlock = getClosestBlockByPoint( this.host, @@ -114,7 +114,7 @@ export class AffineDragHandleWidget extends WidgetComponent { isDraggedElementNote === false ); - if (isDraggedElementNote && result?.type === 'in') return null; + if (isDraggedElementNote && result?.placement === 'in') return null; return result; }; @@ -135,7 +135,7 @@ export class AffineDragHandleWidget extends WidgetComponent { private readonly _reset = () => { this.draggingElements = []; this.dropBlockId = ''; - this.dropType = null; + this.dropPlacement = null; this.lastDragPointerState = null; this.rafID = 0; this.dragging = false; @@ -157,29 +157,29 @@ export class AffineDragHandleWidget extends WidgetComponent { document.documentElement.classList.remove('affine-drag-preview-grabbing'); }; - private readonly _resetDropResult = () => { + private readonly _resetDropTarget = () => { this.dropBlockId = ''; - this.dropType = null; + this.dropPlacement = null; if (this.dropIndicator) this.dropIndicator.rect = null; }; - private readonly _updateDropResult = (dropResult: DropResult | null) => { + private readonly _updateDropTarget = (dropTarget: DropTarget | null) => { if (!this.dropIndicator) return; - this.dropBlockId = dropResult?.modelState.model.id ?? ''; - this.dropType = dropResult?.type ?? null; - if (dropResult?.rect) { + this.dropBlockId = dropTarget?.modelState.model.id ?? ''; + this.dropPlacement = dropTarget?.placement ?? null; + if (dropTarget?.rect) { const offsetParentRect = this.dragHandleContainerOffsetParent.getBoundingClientRect(); - let { left, top } = dropResult.rect; + let { left, top } = dropTarget.rect; left -= offsetParentRect.left; top -= offsetParentRect.top; - const { width, height } = dropResult.rect; + const { width, height } = dropTarget.rect; const rect = Rect.fromLWTH(left, width, top, height); this.dropIndicator.rect = rect; } else { - this.dropIndicator.rect = dropResult?.rect ?? null; + this.dropIndicator.rect = dropTarget?.rect ?? null; } }; @@ -221,7 +221,7 @@ export class AffineDragHandleWidget extends WidgetComponent { dropIndicator: DropIndicator | null = null; - dropType: DroppingType | null = null; + dropPlacement: DropPlacement | null = null; edgelessWatcher = new EdgelessWatcher(this); @@ -298,10 +298,10 @@ export class AffineDragHandleWidget extends WidgetComponent { !closestNoteBlock || isOutOfNoteBlock(this.host, closestNoteBlock, point, this.scale.peek()) ) { - this._resetDropResult(); + this._resetDropTarget(); } else { - const dropResult = this._getDropResult(state); - this._updateDropResult(dropResult); + const dropTarget = this._getDropTarget(state); + this._updateDropTarget(dropTarget); } this.lastDragPointerState = state; diff --git a/blocksuite/affine/widget-drag-handle/src/utils.ts b/blocksuite/affine/widget-drag-handle/src/utils.ts index 20a27f7db..3f6f60510 100644 --- a/blocksuite/affine/widget-drag-handle/src/utils.ts +++ b/blocksuite/affine/widget-drag-handle/src/utils.ts @@ -3,7 +3,7 @@ import type { ParagraphBlockModel } from '@blocksuite/affine-model'; import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { calcDropTarget, - type DropResult, + type DropTarget, findClosestBlockComponent, getBlockProps, getClosestBlockComponentByPoint, @@ -174,7 +174,7 @@ export const getClosestBlockByPoint = ( export const getDropResult = ( event: MouseEvent, scale: number = 1 -): DropResult | null => { +): DropTarget | null => { let dropIndicator = null; const point = new Point(event.x, event.y); const closestBlock = getClosestBlockComponentByPoint(point) as BlockComponent; diff --git a/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts b/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts index aa57a91c4..a0571d57d 100644 --- a/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts +++ b/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts @@ -16,7 +16,7 @@ import { import { calcDropTarget, captureEventTarget, - type DropResult, + type DropTarget, getBlockComponentsExcludeSubtrees, getClosestBlockComponentByPoint, matchFlavours, @@ -290,11 +290,11 @@ export class DragEventWatcher { if (matchFlavours(parent, ['affine:surface'])) { return; } - const result: DropResult | null = calcDropTarget(point, model, element); - if (!result) return; + const target: DropTarget | null = calcDropTarget(point, model, element); + if (!target) return; const index = - parent.children.indexOf(model) + (result.type === 'before' ? 0 : 1); + parent.children.indexOf(model) + (target.placement === 'before' ? 0 : 1); if (matchFlavours(parent, ['affine:note'])) { const snapshot = this._deserializeSnapshot(state);