Skip to content

Commit

Permalink
feat(edgeless): support consistent dragging behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
AyushAgrawal-A2 committed Dec 7, 2023
1 parent 2b5bb47 commit c542fe9
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 57 deletions.
14 changes: 14 additions & 0 deletions packages/blocks/src/_common/utils/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,10 @@ function isEdgelessChildNote({ classList }: Element) {
return classList.contains('edgeless-block-portal-note');
}

function isEdgelessChildImage({ classList }: Element) {
return classList.contains('edgeless-block-portal-image');
}

/**
* Returns the closest block element by a point in the rect.
*
Expand Down Expand Up @@ -717,6 +721,16 @@ export function getHoveringNote(point: Point) {
);
}

/**
* Get hovering note with given a point in edgeless mode.
*/
export function getHoveringImage(point: Point) {
return (
document.elementsFromPoint(point.x, point.y).find(isEdgelessChildImage) ||
null
);
}

/**
* Gets the table of the database.
*/
Expand Down
16 changes: 14 additions & 2 deletions packages/blocks/src/_common/widgets/drag-handle/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,27 @@ export type DragHandleOption = {
startDragging: (
blockElements: BlockElement[],
state: PointerEventState
) => void
) => void,
{
hoveredBlockId,
hoveredBlockPath,
}: { hoveredBlockId: string; hoveredBlockPath: string[] | null }
) => boolean;
onDragMove?: (
state: PointerEventState,
draggingElements?: BlockElement[]
) => boolean;
onDragEnd?: (
state: PointerEventState,
draggingElements: BlockElement[]
{
draggingElements,
dropBlockId,
dropType,
}: {
draggingElements: BlockElement[];
dropBlockId: string;
dropType: DropType | null;
}
) => boolean;
};

Expand Down
159 changes: 139 additions & 20 deletions packages/blocks/src/_common/widgets/drag-handle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import {
type UIEventHandler,
type UIEventStateContext,
} from '@blocksuite/block-std';
import { assertExists, DisposableGroup } from '@blocksuite/global/utils';
import {
assertExists,
assertInstanceOf,
DisposableGroup,
} from '@blocksuite/global/utils';
import type { BlockElement } from '@blocksuite/lit';
import { WidgetElement } from '@blocksuite/lit';
import type { BaseBlockModel } from '@blocksuite/store';
Expand All @@ -13,7 +17,6 @@ import { customElement, query, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';

import {
getBlockElementByModel,
getBlockElementsExcludeSubtrees,
getCurrentNativeRange,
getModelByBlockElement,
Expand All @@ -23,8 +26,13 @@ import {
Point,
Rect,
} from '../../../_common/utils/index.js';
import { DocPageBlockComponent } from '../../../page-block/doc/doc-page-block.js';
import { EdgelessPageBlockComponent } from '../../../page-block/edgeless/edgeless-page-block.js';
import type { NoteBlockModel } from '../../../index.js';
import {
DocPageBlockComponent,
EdgelessPageBlockComponent,
getHoveringImage,
getHoveringNote,
} from '../../../index.js';
import { autoScroll } from '../../../page-block/text-selection/utils.js';
import { DragPreview } from './components/drag-preview.js';
import { DropIndicator } from './components/drop-indicator.js';
Expand Down Expand Up @@ -661,6 +669,66 @@ export class AffineDragHandleWidget extends WidgetElement<
);
};

/**
* When pointer move on edgeless note, should show drag handle
* And update hover block id and path
*/
private _pointerMoveOnEdgelessNote = (ctx: UIEventStateContext) => {
const state = ctx.get('pointerState');
const point = getContainerOffsetPoint(state);

const edgelessBlockPortalNote = getHoveringNote(point);
assertExists(edgelessBlockPortalNote);

const noteBlock = edgelessBlockPortalNote.querySelector('affine-note');
assertExists(noteBlock);

this._hoveredBlockId = noteBlock.model.id;
this._hoveredBlockPath = noteBlock.path;

// If current block is not the last hovered block, show drag handle beside the hovered block
if (
(!this._lastHoveredBlockPath ||
this._hoveredBlockPath.join('|') !==
this._lastHoveredBlockPath?.join('|') ||
this._dragHandleContainer.style.display === 'none') &&
!this._hoverDragHandle
) {
this._show(noteBlock);
this._lastHoveredBlockPath = this._hoveredBlockPath;
}
};

/**
* When pointer move on edgeless note, should show drag handle
* And update hover block id and path
*/
private _pointerMoveOnEdgelessImage = (ctx: UIEventStateContext) => {
const state = ctx.get('pointerState');
const point = getContainerOffsetPoint(state);

const edgelessBlockPortalImage = getHoveringImage(point);
assertExists(edgelessBlockPortalImage);

const imageBlock = edgelessBlockPortalImage.querySelector('affine-image');
assertExists(imageBlock);

this._hoveredBlockId = imageBlock.model.id;
this._hoveredBlockPath = imageBlock.path;

// If current block is not the last hovered block, show drag handle beside the hovered block
if (
(!this._lastHoveredBlockPath ||
this._hoveredBlockPath.join('|') !==
this._lastHoveredBlockPath?.join('|') ||
this._dragHandleContainer.style.display === 'none') &&
!this._hoverDragHandle
) {
this._show(imageBlock);
this._lastHoveredBlockPath = this._hoveredBlockPath;
}
};

/**
* When pointer move on block, should show drag handle
* And update hover block id and path
Expand Down Expand Up @@ -714,7 +782,7 @@ export class AffineDragHandleWidget extends WidgetElement<

const { target } = state.raw;
const element = captureEventTarget(target);
// WHen pointer not on block or on dragging, should do nothing
// When pointer not on block or on dragging, should do nothing
if (!element || this.dragging) {
return;
}
Expand All @@ -732,15 +800,22 @@ export class AffineDragHandleWidget extends WidgetElement<
this.pageBlockElement,
point
);
if (
!closestNoteBlock ||
!this._canEditing(closestNoteBlock as BlockElement) ||
this.outOfNoteBlock(closestNoteBlock, point)
) {
this.hide();

if (!closestNoteBlock || this.outOfNoteBlock(closestNoteBlock, point)) {
// hovering over top level image in edgeless page
if (getHoveringImage(point)) {
this._pointerMoveOnEdgelessImage(ctx);
return true;
}

return;
}

if (!this._canEditing(closestNoteBlock as BlockElement)) {
this._pointerMoveOnEdgelessNote(ctx);
return true;
}

this._pointerMoveOnBlock(ctx);
return true;
};
Expand Down Expand Up @@ -864,22 +939,54 @@ export class AffineDragHandleWidget extends WidgetElement<
return true;
};

private _onDragMove = (ctx: UIEventStateContext) => {
private _onDragMove = (state: PointerEventState) => {
this.clearRaf();

const state = ctx.get('pointerState');
this.rafID = requestAnimationFrame(() => this.updateIndicator(state, true));

return true;
};

private _onDragEnd = () => {
private _onDragEnd = (state: PointerEventState) => {
const targetBlockId = this.dropBlockId;
const dropType = this.dropType;
const draggingElements = this.draggingElements;

this.hide(true);
if (!targetBlockId) return false;

if (!targetBlockId) {
const target = captureEventTarget(state.raw.target);

const isTargetEdgelessContainer =
target?.classList.contains('edgeless') &&
target?.classList.contains('affine-block-children-container');

if (!isTargetEdgelessContainer) {
return false;
}

const selectedBlocks = getBlockElementsExcludeSubtrees(draggingElements)
.map(element => getModelByBlockElement(element))
.filter((x): x is BaseBlockModel => !!x);

if (selectedBlocks.length === 0) {
return false;
}

const edgelessPage = this.pageBlockElement;
assertInstanceOf(edgelessPage, EdgelessPageBlockComponent);

const newNoteId = edgelessPage.addNoteWithPoint(
new Point(state.x, state.y)
);

this.page.moveBlocks(
selectedBlocks,
this.page.getBlockById(newNoteId) as NoteBlockModel
);

return true;
}

// Should make sure drop block id is not in selected blocks
if (
Expand Down Expand Up @@ -922,7 +1029,7 @@ export class AffineDragHandleWidget extends WidgetElement<
assertExists(parent);
// Need to update selection when moving blocks successfully
// Because the block path may be changed after moving
const parentElement = getBlockElementByModel(parent);
const parentElement = this.root.view.viewFromPath('block', [parent.id]);
if (parentElement) {
const newSelectedBlocks = selectedBlocks
.map(block => parentElement.path.concat(block.id))
Expand All @@ -949,7 +1056,12 @@ export class AffineDragHandleWidget extends WidgetElement<

// call default drag start handler if no option return true
for (const option of this.optionRunner.options) {
if (option.onDragStart?.(state, this.startDragging)) {
if (
option.onDragStart?.(state, this.startDragging, {
hoveredBlockId: this._hoveredBlockId,
hoveredBlockPath: this._hoveredBlockPath,
})
) {
return true;
}
}
Expand Down Expand Up @@ -980,7 +1092,7 @@ export class AffineDragHandleWidget extends WidgetElement<
}

// call default drag move handler if no option return true
return this._onDragMove(ctx);
return this._onDragMove(state);
};

/**
Expand All @@ -997,17 +1109,24 @@ export class AffineDragHandleWidget extends WidgetElement<
this.hide(true);
return false;
}

const state = ctx.get('pointerState');

for (const option of this.optionRunner.options) {
if (option.onDragEnd?.(state, this.draggingElements)) {
if (
option.onDragEnd?.(state, {
draggingElements: this.draggingElements,
dropBlockId: this.dropBlockId,
dropType: this.dropType,
})
) {
this.hide(true);
return true;
}
}

//call default drag end handler if no option return true
return this._onDragEnd();
return this._onDragEnd(state);
};

private _pointerOutHandler: UIEventHandler = ctx => {
Expand Down
2 changes: 1 addition & 1 deletion packages/blocks/src/attachment-block/attachment-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export class AttachmentBlockComponent extends BlockElement<AttachmentBlockModel>
AffineDragHandleWidget.registerOption({
flavour: AttachmentBlockSchema.model.flavour,
onDragStart: (state, startDragging) => {
// Check if start dragging from the image block
// Check if start dragging from the attachment block
const target = captureEventTarget(state.raw.target);
const attachmentBlock = target?.closest('affine-attachment');
if (!attachmentBlock) return false;
Expand Down
2 changes: 1 addition & 1 deletion packages/blocks/src/bookmark-block/bookmark-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class BookmarkBlockComponent extends BlockElement<BookmarkBlockModel> {
AffineDragHandleWidget.registerOption({
flavour: BookmarkBlockSchema.model.flavour,
onDragStart: (state, startDragging) => {
// Check if start dragging from the image block
// Check if start dragging from the bookmark block
const target = captureEventTarget(state.raw.target);
const bookmarkBlock = target?.closest('affine-bookmark');
if (!bookmarkBlock) return false;
Expand Down
2 changes: 1 addition & 1 deletion packages/blocks/src/database-block/database-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export class DatabaseBlockComponent extends BlockElement<DatabaseBlockModel> {
}
return false;
},
onDragEnd: (state, draggingElements) => {
onDragEnd: (state, { draggingElements }) => {
const target = state.raw.target;
const view = this.view;
if (
Expand Down
Loading

0 comments on commit c542fe9

Please sign in to comment.