Skip to content

Commit

Permalink
feat(edgeless): support consistent dragging behavior (toeverything#5613)
Browse files Browse the repository at this point in the history
  • Loading branch information
AyushAgrawal-A2 authored and nawbc committed Dec 15, 2023
1 parent a603a7e commit 4e67eea
Show file tree
Hide file tree
Showing 52 changed files with 7,283 additions and 6,449 deletions.
5 changes: 0 additions & 5 deletions packages/blocks/src/_common/components/file-drop-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export type onDropProps = {

export type FileDropOptions = {
flavour: string;
maxFileSize?: number;
onDrop?: ({ files, targetModel, place, point }: onDropProps) => void;
};

Expand Down Expand Up @@ -81,10 +80,6 @@ export class FileDropManager {
return targetModel;
}

get maxFileSize(): number {
return this._fileDropOptions.maxFileSize ?? 10 * 1000 * 1000; // default to 10MB
}

onDragOver = (event: DragEvent) => {
event.preventDefault();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
focusBlockByModel,
focusTitle,
} from '../../../_common/utils/selection.js';
import { type ListBlockModel, type PageBlockModel } from '../../../models.js';
import type { ListBlockModel } from '../../../list-block/index.js';
import type { PageBlockModel } from '../../../page-block/index.js';
import type { ExtendedModel } from '../../types.js';

/**
Expand Down
116 changes: 111 additions & 5 deletions packages/blocks/src/_common/embed-block-helper/embed-block-element.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import type { BlockService } from '@blocksuite/block-std';
import { assertExists } from '@blocksuite/global/utils';
import { BlockElement } from '@blocksuite/lit';
import type { BaseBlockModel } from '@blocksuite/store';
import type { TemplateResult } from 'lit';
import { html } from 'lit';
import { html, render } from 'lit';
import { styleMap } from 'lit/directives/style-map.js';

import type { DragHandleOption } from '../../page-block/widgets/drag-handle/config.js';
import { AffineDragHandleWidget } from '../../page-block/widgets/drag-handle/drag-handle.js';
import {
captureEventTarget,
convertDragPreviewDocToEdgeless,
convertDragPreviewEdgelessToDoc,
} from '../../page-block/widgets/drag-handle/utils.js';
import { Bound } from '../../surface-block/index.js';
import type { EdgelessSelectableProps } from '../edgeless/mixin/index.js';
import { type BlockModels, matchFlavours } from '../utils/index.js';

export class EmbedBlockElement<
Model extends
BaseBlockModel<EdgelessSelectableProps> = BaseBlockModel<EdgelessSelectableProps>,
Service extends BlockService = BlockService,
WidgetName extends string = string,
> extends BlockElement<Model, Service, WidgetName> {
protected get _isInSurface() {
const parent = this.host.querySelector('affine-edgeless-page');
return !!parent;
protected _isInSurface = false;

protected _width = 400;
protected _height = 200;

get isInSurface() {
return this._isInSurface;
}

get surface() {
if (!this._isInSurface) return null;

return this.host.querySelector('affine-surface');
}

Expand All @@ -48,4 +60,98 @@ export class EmbedBlockElement<
</div>
`;
};

private _dragHandleOption: DragHandleOption = {
flavour: /affine:embed-*/,
edgeless: true,
onDragStart: ({ state, startDragging, anchorBlockPath }) => {
if (!anchorBlockPath) return false;
const anchorComponent = this.std.view.viewFromPath(
'block',
anchorBlockPath
);
if (
!anchorComponent ||
!matchFlavours(anchorComponent.model, [
this.flavour as keyof BlockModels,
])
)
return false;

const blockComponent = anchorComponent as this;
const isInSurface = blockComponent.isInSurface;
if (!isInSurface) {
this.host.selection.setGroup('block', [
this.host.selection.getInstance('block', {
path: blockComponent.path,
}),
]);
startDragging([blockComponent], state);
return true;
}

const element = captureEventTarget(state.raw.target);
const insideDragHandle = !!element?.closest('affine-drag-handle-widget');
if (!insideDragHandle) return false;

const embedPortal = blockComponent.closest(
'.edgeless-block-portal-embed'
);
assertExists(embedPortal);
const dragPreviewEl = embedPortal.cloneNode() as HTMLElement;
dragPreviewEl.style.transform = '';
render(
blockComponent.host.renderModel(blockComponent.model),
dragPreviewEl
);

startDragging([blockComponent], state, dragPreviewEl);
return true;
},
onDragEnd: props => {
const { state, draggingElements } = props;
if (
draggingElements.length !== 1 ||
!matchFlavours(draggingElements[0].model, [
this.flavour as keyof BlockModels,
])
)
return false;

const blockComponent = draggingElements[0] as this;
const isInSurface = blockComponent.isInSurface;
const target = captureEventTarget(state.raw.target);
const isTargetEdgelessContainer =
target?.classList.contains('edgeless') &&
target?.classList.contains('affine-block-children-container');

if (isInSurface) {
return convertDragPreviewEdgelessToDoc({
blockComponent,
...props,
});
} else if (isTargetEdgelessContainer) {
return convertDragPreviewDocToEdgeless({
blockComponent,
cssSelector: '.embed-block-container',
width: this._width,
height: this._height,
...props,
});
}

return false;
},
};

override connectedCallback() {
super.connectedCallback();

const parent = this.host.page.getParent(this.model);
this._isInSurface = parent?.flavour === 'affine:surface';

this.disposables.add(
AffineDragHandleWidget.registerOption(this._dragHandleOption)
);
}
}
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 @@ -411,6 +411,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 @@ -701,6 +705,16 @@ export function getHoveringNote(point: Point) {
);
}

/**
* Get hovering top level image 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
2 changes: 1 addition & 1 deletion packages/blocks/src/_legacy/clipboard/utils/commons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function generateClipboardInfo(
childrenJson.push(json);
}

const service = await getService(model.flavour);
const service = getService(model.flavour);

const html = await service.block2html(model, {
childText: childrenHtml.join(''),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class BookmarkBlockService extends BaseService<BookmarkBlockModel> {
</figure>
`;
}

override block2Text(block: BookmarkBlockModel): string {
return block.url;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/blocks/src/attachment-block/attachment-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ export class AttachmentBlockComponent extends BlockElement<AttachmentBlockModel>
this._disposables.add(
AffineDragHandleWidget.registerOption({
flavour: AttachmentBlockSchema.model.flavour,
onDragStart: (state, startDragging) => {
// Check if start dragging from the image block
onDragStart: ({ state, startDragging }) => {
// 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
19 changes: 1 addition & 18 deletions packages/blocks/src/attachment-block/attachment-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
FileDropManager,
type FileDropOptions,
} from '../_common/components/file-drop-manager.js';
import { toast } from '../_common/components/toast.js';
import { humanFileSize } from '../_common/utils/math.js';
import { matchFlavours } from '../_common/utils/model.js';
import type { AttachmentBlockModel } from './attachment-model.js';
import { addSiblingAttachmentBlock } from './utils.js';
Expand All @@ -15,7 +13,6 @@ export class AttachmentService extends BlockService<AttachmentBlockModel> {

private _fileDropOptions: FileDropOptions = {
flavour: this.flavour,
maxFileSize: this.maxFileSize,
onDrop: async ({ files, targetModel, place }) => {
if (!files.length || !targetModel) return false;
if (matchFlavours(targetModel, ['affine:surface'])) return false;
Expand All @@ -25,22 +22,8 @@ export class AttachmentService extends BlockService<AttachmentBlockModel> {
file => !file.type.startsWith('image/')
);

const isSizeExceeded = attachmentFiles.some(
file => file.size > this.maxFileSize
);
if (isSizeExceeded) {
toast(
`You can only upload files less than ${humanFileSize(
this.maxFileSize,
true,
0
)}`
);
return true;
}

attachmentFiles.forEach(file =>
addSiblingAttachmentBlock(file, targetModel, this.maxFileSize, place)
addSiblingAttachmentBlock(file, this.maxFileSize, targetModel, place)
);
return true;
},
Expand Down
2 changes: 1 addition & 1 deletion packages/blocks/src/attachment-block/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ export async function uploadBlobForAttachment(
*/
export async function addSiblingAttachmentBlock(
file: File,
model: BaseBlockModel,
maxFileSize: number,
model: BaseBlockModel,
place: 'before' | 'after' = 'after'
): Promise<string | null> {
if (file.size > maxFileSize) {
Expand Down
Loading

0 comments on commit 4e67eea

Please sign in to comment.