refactor: remove legacy drag handle logic (#9246)

This commit is contained in:
Saul-Mirone
2024-12-23 08:13:04 +00:00
parent a187f23452
commit 23dcaa9cb7
14 changed files with 237 additions and 718 deletions

View File

@@ -1,5 +1,7 @@
import type { DragIndicator } from '@blocksuite/affine-components/drag-indicator';
import {
calcDropTarget,
type DropResult,
getClosestBlockComponentByPoint,
isInsidePageEditor,
matchFlavours,
@@ -9,8 +11,6 @@ import type { IVec } from '@blocksuite/global/utils';
import { assertExists, Point } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store';
import { calcDropTarget, type DropResult } from '../../_common/utils/index.js';
export type onDropProps = {
files: File[];
targetModel: BlockModel | null;

View File

@@ -1,174 +0,0 @@
import {
getClosestBlockComponentByElement,
getRectByBlockComponent,
matchFlavours,
} from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std';
import { type Point, Rect } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store';
import type { EditingState } from '../types.js';
import { DropFlags, getDropRectByPoint } from './query.js';
/**
* A dropping type.
*/
export type DroppingType = 'none' | 'before' | 'after' | 'database';
export type DropResult = {
type: DroppingType;
rect: Rect;
modelState: EditingState;
};
/**
* Calculates the drop target.
*/
export function calcDropTarget(
point: Point,
model: BlockModel,
element: Element,
draggingElements: BlockComponent[] = [],
scale: number = 1,
flavour: string | null = null // for block-hub
): DropResult | null {
const schema = model.doc.getSchemaByFlavour('affine:database');
const children = schema?.model.children ?? [];
let shouldAppendToDatabase = true;
if (children.length) {
if (draggingElements.length) {
shouldAppendToDatabase = draggingElements
.map(el => el.model)
.every(m => children.includes(m.flavour));
} else if (flavour) {
shouldAppendToDatabase = children.includes(flavour);
}
}
if (!shouldAppendToDatabase && !matchFlavours(model, ['affine:database'])) {
const databaseBlockComponent = element.closest('affine-database');
if (databaseBlockComponent) {
element = databaseBlockComponent;
model = databaseBlockComponent.model;
}
}
let type: DroppingType = 'none';
const height = 3 * scale;
const { rect: domRect, flag } = getDropRectByPoint(point, model, element);
if (flag === DropFlags.EmptyDatabase) {
// empty database
const rect = Rect.fromDOMRect(domRect);
rect.top -= height / 2;
rect.height = height;
type = 'database';
return {
type,
rect,
modelState: {
model,
rect: domRect,
element: element as BlockComponent,
},
};
} else if (flag === DropFlags.Database) {
// not empty database
const distanceToTop = Math.abs(domRect.top - point.y);
const distanceToBottom = Math.abs(domRect.bottom - point.y);
const before = distanceToTop < distanceToBottom;
type = before ? 'before' : 'after';
return {
type,
rect: Rect.fromLWTH(
domRect.left,
domRect.width,
(before ? domRect.top - 1 : domRect.bottom) - height / 2,
height
),
modelState: {
model,
rect: domRect,
element: element as BlockComponent,
},
};
}
const distanceToTop = Math.abs(domRect.top - point.y);
const distanceToBottom = Math.abs(domRect.bottom - point.y);
const before = distanceToTop < distanceToBottom;
type = before ? 'before' : 'after';
let offsetY = 4;
if (type === 'before') {
// before
let prev;
let prevRect;
prev = element.previousElementSibling;
if (prev) {
if (
draggingElements.length &&
prev === draggingElements[draggingElements.length - 1]
) {
type = 'none';
} else {
prevRect = getRectByBlockComponent(prev);
}
} else {
prev = element.parentElement?.previousElementSibling;
if (prev) {
prevRect = prev.getBoundingClientRect();
}
}
if (prevRect) {
offsetY = (domRect.top - prevRect.bottom) / 2;
}
} else {
// after
let next;
let nextRect;
next = element.nextElementSibling;
if (next) {
if (draggingElements.length && next === draggingElements[0]) {
type = 'none';
next = null;
}
} else {
next = getClosestBlockComponentByElement(
element.parentElement
)?.nextElementSibling;
}
if (next) {
nextRect = getRectByBlockComponent(next);
offsetY = (nextRect.top - domRect.bottom) / 2;
}
}
if (type === 'none') return null;
let top = domRect.top;
if (type === 'before') {
top -= offsetY;
} else {
top += domRect.height + offsetY;
}
return {
type,
rect: Rect.fromLWTH(domRect.left, domRect.width, top - height / 2, height),
modelState: {
model,
rect: domRect,
element: element as BlockComponent,
},
};
}

View File

@@ -1,6 +1,5 @@
// Compat with SSR
export * from '../types.js';
export * from './drag-and-drop.js';
export * from './query.js';
export {
createButtonPopper,

View File

@@ -1,16 +1,8 @@
import {
getRectByBlockComponent,
matchFlavours,
} from '@blocksuite/affine-shared/utils';
import { BLOCK_ID_ATTR, type EditorHost } from '@blocksuite/block-std';
import type { Point } from '@blocksuite/global/utils';
import { assertExists } from '@blocksuite/global/utils';
import type { EditorHost } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';
import type { RootBlockComponent } from '../../index.js';
const ATTR_SELECTOR = `[${BLOCK_ID_ATTR}]`;
/**
* This function is used to build model's "normal" block path.
* If this function does not meet your needs, you may need to build path manually to satisfy your needs.
@@ -59,150 +51,6 @@ export function getBlockComponentByModel(
return editorHost.view.getBlock(model.id);
}
function isEdgelessChildNote({ classList }: Element) {
return classList.contains('note-background');
}
/**
* Get hovering note with given a point in edgeless mode.
*/
export function getHoveringNote(point: Point) {
return (
document.elementsFromPoint(point.x, point.y).find(isEdgelessChildNote) ||
null
);
}
/**
* Gets the table of the database.
*/
function getDatabaseBlockTableElement(element: Element) {
return element.querySelector('.affine-database-block-table');
}
/**
* Gets the column header of the database.
*/
function getDatabaseBlockColumnHeaderElement(element: Element) {
return element.querySelector('.affine-database-column-header');
}
/**
* Gets the rows of the database.
*/
function getDatabaseBlockRowsElement(element: Element) {
return element.querySelector('.affine-database-block-rows');
}
/**
* Returns a flag for the drop target.
*/
export enum DropFlags {
Normal,
Database,
EmptyDatabase,
}
/**
* Gets the drop rect by block and point.
*/
export function getDropRectByPoint(
point: Point,
model: BlockModel,
element: Element
): {
rect: DOMRect;
flag: DropFlags;
} {
const result = {
rect: getRectByBlockComponent(element),
flag: DropFlags.Normal,
};
const isDatabase = matchFlavours(model, ['affine:database']);
if (isDatabase) {
const table = getDatabaseBlockTableElement(element);
if (!table) {
return result;
}
let bounds = table.getBoundingClientRect();
if (model.isEmpty.value) {
result.flag = DropFlags.EmptyDatabase;
if (point.y < bounds.top) return result;
const header = getDatabaseBlockColumnHeaderElement(element);
assertExists(header);
bounds = header.getBoundingClientRect();
result.rect = new DOMRect(
result.rect.left,
bounds.bottom,
result.rect.width,
1
);
} else {
result.flag = DropFlags.Database;
const rows = getDatabaseBlockRowsElement(element);
assertExists(rows);
const rowsBounds = rows.getBoundingClientRect();
if (point.y < rowsBounds.top || point.y > rowsBounds.bottom)
return result;
const elements = document.elementsFromPoint(point.x, point.y);
const len = elements.length;
let e;
let i = 0;
for (; i < len; i++) {
e = elements[i];
if (e.classList.contains('affine-database-block-row-cell-content')) {
result.rect = getCellRect(e, bounds);
return result;
}
if (e.classList.contains('affine-database-block-row')) {
e = e.querySelector(ATTR_SELECTOR);
assertExists(e);
result.rect = getCellRect(e, bounds);
return result;
}
}
}
} else {
const parent = element.parentElement;
if (parent?.classList.contains('affine-database-block-row-cell-content')) {
result.flag = DropFlags.Database;
result.rect = getCellRect(parent);
return result;
}
}
return result;
}
function getCellRect(element: Element, bounds?: DOMRect) {
if (!bounds) {
const table = element.closest('.affine-database-block-table');
assertExists(table);
bounds = table.getBoundingClientRect();
}
// affine-database-block-row-cell
const col = element.parentElement;
assertExists(col);
// affine-database-block-row
const row = col.parentElement;
assertExists(row);
const colRect = col.getBoundingClientRect();
return new DOMRect(
bounds.left,
colRect.top,
colRect.right - bounds.left,
colRect.height
);
}
/**
* Return `true` if the element has class name in the class list.
*/