chore: dnd cleanup (#9899)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { FileDropConfigExtension } from '@blocksuite/affine-components/drag-indicator';
|
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
|
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
|
||||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
popMenu,
|
popMenu,
|
||||||
popupTargetFromElement,
|
popupTargetFromElement,
|
||||||
} from '@blocksuite/affine-components/context-menu';
|
} from '@blocksuite/affine-components/context-menu';
|
||||||
import { DragIndicator } from '@blocksuite/affine-components/drag-indicator';
|
import { DropIndicator } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import { PeekViewProvider } from '@blocksuite/affine-components/peek';
|
import { PeekViewProvider } from '@blocksuite/affine-components/peek';
|
||||||
import { toast } from '@blocksuite/affine-components/toast';
|
import { toast } from '@blocksuite/affine-components/toast';
|
||||||
import type { DatabaseBlockModel } from '@blocksuite/affine-model';
|
import type { DatabaseBlockModel } from '@blocksuite/affine-model';
|
||||||
@@ -241,7 +241,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
indicator = new DragIndicator();
|
indicator = new DropIndicator();
|
||||||
|
|
||||||
onDrag = (evt: MouseEvent, id: string): (() => void) => {
|
onDrag = (evt: MouseEvent, id: string): (() => void) => {
|
||||||
const result = getDropResult(evt);
|
const result = getDropResult(evt);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FileDropConfigExtension } from '@blocksuite/affine-components/drag-indicator';
|
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import { ImageBlockSchema, MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
|
import { ImageBlockSchema, MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
|
||||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
"./caption": "./src/caption/index.ts",
|
"./caption": "./src/caption/index.ts",
|
||||||
"./context-menu": "./src/context-menu/index.ts",
|
"./context-menu": "./src/context-menu/index.ts",
|
||||||
"./date-picker": "./src/date-picker/index.ts",
|
"./date-picker": "./src/date-picker/index.ts",
|
||||||
"./drag-indicator": "./src/drag-indicator/index.ts",
|
"./drop-indicator": "./src/drop-indicator/index.ts",
|
||||||
"./filterable-list": "./src/filterable-list/index.ts",
|
"./filterable-list": "./src/filterable-list/index.ts",
|
||||||
"./virtual-keyboard": "./src/virtual-keyboard/index.ts",
|
"./virtual-keyboard": "./src/virtual-keyboard/index.ts",
|
||||||
"./toggle-button": "./src/toggle-button/index.ts",
|
"./toggle-button": "./src/toggle-button/index.ts",
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import type { Rect } from '@blocksuite/global/utils';
|
|
||||||
import { css, html, LitElement } from 'lit';
|
|
||||||
import { property } from 'lit/decorators.js';
|
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
|
||||||
|
|
||||||
export class DragIndicator extends LitElement {
|
|
||||||
static override styles = css`
|
|
||||||
.affine-drag-indicator {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background: var(--affine-primary-color);
|
|
||||||
transition-property: width, height, transform;
|
|
||||||
transition-duration: 100ms;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-delay: 0s;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
override render() {
|
|
||||||
if (!this.rect) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const { left, top, width, height } = this.rect;
|
|
||||||
const style = styleMap({
|
|
||||||
width: `${width}px`,
|
|
||||||
height: `${height}px`,
|
|
||||||
transform: `translate(${left}px, ${top}px)`,
|
|
||||||
});
|
|
||||||
return html`<div class="affine-drag-indicator" style=${style}></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
accessor rect: Rect | null = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
'affine-drag-indicator': DragIndicator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,14 +25,12 @@ export class DropIndicator extends LitElement {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentRect = this.parentElement!.getBoundingClientRect();
|
|
||||||
const { left, top, width, height } = this.rect;
|
const { left, top, width, height } = this.rect;
|
||||||
|
|
||||||
const style = styleMap({
|
const style = styleMap({
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
top: `${top - parentRect.y}px`,
|
top: `${top}px`,
|
||||||
left: `${left - parentRect.x}px`,
|
left: `${left}px`,
|
||||||
});
|
});
|
||||||
|
|
||||||
return html`<div class="affine-drop-indicator" style=${style}></div>`;
|
return html`<div class="affine-drop-indicator" style=${style}></div>`;
|
||||||
@@ -17,7 +17,7 @@ import { Point, throttle } from '@blocksuite/global/utils';
|
|||||||
import type { BlockModel, ExtensionType } from '@blocksuite/store';
|
import type { BlockModel, ExtensionType } from '@blocksuite/store';
|
||||||
import { computed, signal } from '@preact/signals-core';
|
import { computed, signal } from '@preact/signals-core';
|
||||||
|
|
||||||
import type { DragIndicator } from './drag-indicator';
|
import { DropIndicator } from './drop-indicator';
|
||||||
|
|
||||||
export type DropProps = {
|
export type DropProps = {
|
||||||
std: BlockStdScope;
|
std: BlockStdScope;
|
||||||
@@ -39,20 +39,7 @@ export type FileDropOptions = {
|
|||||||
export class FileDropExtension extends LifeCycleWatcher {
|
export class FileDropExtension extends LifeCycleWatcher {
|
||||||
static override readonly key = 'FileDropExtension';
|
static override readonly key = 'FileDropExtension';
|
||||||
|
|
||||||
static get indicator() {
|
indicator: DropIndicator = new DropIndicator();
|
||||||
let indicator = document.querySelector<DragIndicator>(
|
|
||||||
'affine-drag-indicator'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!indicator) {
|
|
||||||
indicator = document.createElement(
|
|
||||||
'affine-drag-indicator'
|
|
||||||
) as DragIndicator;
|
|
||||||
document.body.append(indicator);
|
|
||||||
}
|
|
||||||
|
|
||||||
return indicator;
|
|
||||||
}
|
|
||||||
|
|
||||||
dragging$ = signal(false);
|
dragging$ = signal(false);
|
||||||
|
|
||||||
@@ -166,10 +153,17 @@ export class FileDropExtension extends LifeCycleWatcher {
|
|||||||
return this.std.host;
|
return this.std.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override unmounted(): void {
|
||||||
|
super.unmounted();
|
||||||
|
this.indicator.remove();
|
||||||
|
}
|
||||||
|
|
||||||
override mounted() {
|
override mounted() {
|
||||||
super.mounted();
|
super.mounted();
|
||||||
const std = this.std;
|
const std = this.std;
|
||||||
|
|
||||||
|
std.host.ownerDocument.body.append(this.indicator);
|
||||||
|
|
||||||
std.event.disposables.add(
|
std.event.disposables.add(
|
||||||
this.point$.subscribe(
|
this.point$.subscribe(
|
||||||
throttle(
|
throttle(
|
||||||
@@ -198,7 +192,7 @@ export class FileDropExtension extends LifeCycleWatcher {
|
|||||||
|
|
||||||
std.event.disposables.add(
|
std.event.disposables.add(
|
||||||
this.dropTarget$.subscribe(target => {
|
this.dropTarget$.subscribe(target => {
|
||||||
FileDropExtension.indicator.rect = this._disableIndicator
|
this.indicator.rect = this._disableIndicator
|
||||||
? null
|
? null
|
||||||
: (target?.rect ?? null);
|
: (target?.rect ?? null);
|
||||||
})
|
})
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DragIndicator } from './drag-indicator';
|
import { DropIndicator } from './drop-indicator';
|
||||||
export {
|
export {
|
||||||
type DropProps,
|
type DropProps,
|
||||||
FileDropConfigExtension,
|
FileDropConfigExtension,
|
||||||
@@ -6,8 +6,8 @@ export {
|
|||||||
type FileDropOptions,
|
type FileDropOptions,
|
||||||
} from './file-drop-manager';
|
} from './file-drop-manager';
|
||||||
|
|
||||||
export { DragIndicator };
|
export { DropIndicator };
|
||||||
|
|
||||||
export function effects() {
|
export function effects() {
|
||||||
customElements.define('affine-drag-indicator', DragIndicator);
|
customElements.define('affine-drop-indicator', DropIndicator);
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,9 @@ export class AffineDragHandleWidget extends WidgetComponent<RootBlockModel> {
|
|||||||
|
|
||||||
private _anchorModelDisposables: DisposableGroup | null = null;
|
private _anchorModelDisposables: DisposableGroup | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to handle drag behavior
|
||||||
|
*/
|
||||||
private readonly _dragEventWatcher = new DragEventWatcher(this);
|
private readonly _dragEventWatcher = new DragEventWatcher(this);
|
||||||
|
|
||||||
private readonly _handleEventWatcher = new HandleEventWatcher(this);
|
private readonly _handleEventWatcher = new HandleEventWatcher(this);
|
||||||
@@ -54,11 +57,6 @@ export class AffineDragHandleWidget extends WidgetComponent<RootBlockModel> {
|
|||||||
this.isTopLevelDragHandleVisible = false;
|
this.isTopLevelDragHandleVisible = false;
|
||||||
|
|
||||||
this.pointerEventWatcher.reset();
|
this.pointerEventWatcher.reset();
|
||||||
this._resetCursor();
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _resetCursor = () => {
|
|
||||||
document.documentElement.classList.remove('affine-drag-preview-grabbing');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
anchorBlockId = signal<string | null>(null);
|
anchorBlockId = signal<string | null>(null);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { DropIndicator } from './components/drop-indicator';
|
|
||||||
import { AFFINE_DRAG_HANDLE_WIDGET } from './consts';
|
import { AFFINE_DRAG_HANDLE_WIDGET } from './consts';
|
||||||
import { AffineDragHandleWidget } from './drag-handle';
|
import { AffineDragHandleWidget } from './drag-handle';
|
||||||
|
|
||||||
export function effects() {
|
export function effects() {
|
||||||
customElements.define('affine-drop-indicator', DropIndicator);
|
|
||||||
customElements.define(AFFINE_DRAG_HANDLE_WIDGET, AffineDragHandleWidget);
|
customElements.define(AFFINE_DRAG_HANDLE_WIDGET, AffineDragHandleWidget);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
addNoteAtPoint,
|
addNoteAtPoint,
|
||||||
getSurfaceBlock,
|
getSurfaceBlock,
|
||||||
} from '@blocksuite/affine-block-surface';
|
} from '@blocksuite/affine-block-surface';
|
||||||
|
import { DropIndicator } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import type { EmbedCardStyle, NoteBlockModel } from '@blocksuite/affine-model';
|
import type { EmbedCardStyle, NoteBlockModel } from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
|
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
|
||||||
@@ -23,7 +24,6 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/utils';
|
} from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
import {
|
||||||
type BlockComponent,
|
type BlockComponent,
|
||||||
BlockSelection,
|
|
||||||
type BlockStdScope,
|
type BlockStdScope,
|
||||||
type DragFromBlockSuite,
|
type DragFromBlockSuite,
|
||||||
type DragPayload,
|
type DragPayload,
|
||||||
@@ -34,7 +34,6 @@ import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
|||||||
import { Bound, last, Point, Rect } from '@blocksuite/global/utils';
|
import { Bound, last, Point, Rect } from '@blocksuite/global/utils';
|
||||||
import { Slice, type SliceSnapshot } from '@blocksuite/store';
|
import { Slice, type SliceSnapshot } from '@blocksuite/store';
|
||||||
|
|
||||||
import { DropIndicator } from '../components/drop-indicator.js';
|
|
||||||
import type { AffineDragHandleWidget } from '../drag-handle.js';
|
import type { AffineDragHandleWidget } from '../drag-handle.js';
|
||||||
import { PreviewHelper } from '../helpers/preview-helper.js';
|
import { PreviewHelper } from '../helpers/preview-helper.js';
|
||||||
import { newIdCrossDoc } from '../middleware/new-id-cross-doc.js';
|
import { newIdCrossDoc } from '../middleware/new-id-cross-doc.js';
|
||||||
@@ -66,6 +65,8 @@ export class DragEventWatcher {
|
|||||||
|
|
||||||
previewHelper = new PreviewHelper(this.widget);
|
previewHelper = new PreviewHelper(this.widget);
|
||||||
|
|
||||||
|
dropTargetCleanUps: Map<string, (() => void)[]> = new Map();
|
||||||
|
|
||||||
get host() {
|
get host() {
|
||||||
return this.widget.host;
|
return this.widget.host;
|
||||||
}
|
}
|
||||||
@@ -257,25 +258,6 @@ export class DragEventWatcher {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _getDraggedBlock = (draggedBlock: BlockComponent) => {
|
|
||||||
return this._selectAndSetDraggingBlock(draggedBlock);
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _selectAndSetDraggingBlock = (
|
|
||||||
hoveredBlock: BlockComponent
|
|
||||||
) => {
|
|
||||||
this.std.selection.setGroup('note', [
|
|
||||||
this.std.selection.create(BlockSelection, {
|
|
||||||
blockId: hoveredBlock.blockId,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
models: [hoveredBlock.model],
|
|
||||||
snapshot: this._toSnapshot([hoveredBlock]),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _getSnapshotFromHoveredBlocks = () => {
|
private readonly _getSnapshotFromHoveredBlocks = () => {
|
||||||
const hoverBlock = this.widget.anchorBlockComponent.peek()!;
|
const hoverBlock = this.widget.anchorBlockComponent.peek()!;
|
||||||
const isInSurface = isGfxBlockComponent(hoverBlock);
|
const isInSurface = isGfxBlockComponent(hoverBlock);
|
||||||
@@ -620,41 +602,11 @@ export class DragEventWatcher {
|
|||||||
return std === this.std;
|
return std === this.std;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch() {
|
private _makeDraggable(target: HTMLElement) {
|
||||||
this.widget.handleEvent('pointerDown', ctx => {
|
|
||||||
const state = ctx.get('pointerState');
|
|
||||||
const event = state.raw;
|
|
||||||
const target = captureEventTarget(event.target);
|
|
||||||
if (!target) return;
|
|
||||||
|
|
||||||
if (this.widget.contains(target)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.widget.handleEvent('dragStart', ctx => {
|
|
||||||
const state = ctx.get('pointerState');
|
|
||||||
const event = state.raw;
|
|
||||||
const target = captureEventTarget(event.target);
|
|
||||||
if (!target) return;
|
|
||||||
|
|
||||||
if (this.widget.contains(target)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
const widget = this.widget;
|
|
||||||
const std = this.std;
|
const std = this.std;
|
||||||
const disposables = widget.disposables;
|
|
||||||
const scrollable = getScrollContainer(this.host);
|
|
||||||
|
|
||||||
disposables.add(
|
return std.dnd.draggable<DragBlockEntity>({
|
||||||
std.dnd.draggable<DragBlockEntity>({
|
element: target,
|
||||||
element: this.widget,
|
|
||||||
canDrag: () => {
|
canDrag: () => {
|
||||||
const hoverBlock = this.widget.anchorBlockComponent.peek();
|
const hoverBlock = this.widget.anchorBlockComponent.peek();
|
||||||
return hoverBlock ? true : false;
|
return hoverBlock ? true : false;
|
||||||
@@ -684,23 +636,57 @@ export class DragEventWatcher {
|
|||||||
snapshot,
|
snapshot,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
if (scrollable) {
|
private _makeDropTarget(view: BlockComponent) {
|
||||||
disposables.add(
|
if (view.model.role !== 'content' && view.model.role !== 'hub') {
|
||||||
std.dnd.autoScroll<DragBlockEntity>({
|
return;
|
||||||
element: scrollable,
|
}
|
||||||
canScroll: ({ source }) => {
|
|
||||||
return source.data?.bsEntity?.type === 'blocks';
|
const widget = this.widget;
|
||||||
},
|
const cleanups: (() => void)[] = [];
|
||||||
})
|
|
||||||
|
cleanups.push(
|
||||||
|
this.std.dnd.dropTarget<
|
||||||
|
DragBlockEntity,
|
||||||
|
{
|
||||||
|
modelId: string;
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
element: view,
|
||||||
|
getIsSticky: () => true,
|
||||||
|
canDrop: ({ source }) => {
|
||||||
|
if (source.data.bsEntity?.type === 'blocks') {
|
||||||
|
return (
|
||||||
|
source.data.from?.docId !== widget.doc.id ||
|
||||||
|
source.data.bsEntity.modelIds.every(id => id !== view.model.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to handle drag move and drop
|
return false;
|
||||||
disposables.add(
|
},
|
||||||
std.dnd.monitor<DragBlockEntity>({
|
setDropData: () => {
|
||||||
|
return {
|
||||||
|
modelId: view.model.id,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matchFlavours(view.model, ['affine:attachment', 'affine:bookmark'])) {
|
||||||
|
cleanups.push(this._makeDraggable(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dropTargetCleanUps.has(view.model.id)) {
|
||||||
|
this.dropTargetCleanUps.get(view.model.id)!.forEach(clean => clean());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dropTargetCleanUps.set(view.model.id, cleanups);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _monitorBlockDrag() {
|
||||||
|
return this.std.dnd.monitor<DragBlockEntity>({
|
||||||
canMonitor: ({ source }) => {
|
canMonitor: ({ source }) => {
|
||||||
const entity = source.data?.bsEntity;
|
const entity = source.data?.bsEntity;
|
||||||
|
|
||||||
@@ -768,104 +754,76 @@ export class DragEventWatcher {
|
|||||||
target.element as BlockComponent
|
target.element as BlockComponent
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
|
watch() {
|
||||||
|
this.widget.handleEvent('pointerDown', ctx => {
|
||||||
|
const state = ctx.get('pointerState');
|
||||||
|
const event = state.raw;
|
||||||
|
const target = captureEventTarget(event.target);
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
if (this.widget.contains(target)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let dropTargetCleanUps: Map<string, (() => void)[]> = new Map();
|
|
||||||
const makeBlockComponentDropTarget = (view: BlockComponent) => {
|
|
||||||
if (view.model.role !== 'content' && view.model.role !== 'hub') {
|
|
||||||
return;
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.widget.handleEvent('dragStart', ctx => {
|
||||||
|
const state = ctx.get('pointerState');
|
||||||
|
const event = state.raw;
|
||||||
|
const target = captureEventTarget(event.target);
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
if (this.widget.contains(target)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanups: (() => void)[] = [];
|
|
||||||
|
|
||||||
cleanups.push(
|
|
||||||
std.dnd.dropTarget<
|
|
||||||
DragBlockEntity,
|
|
||||||
{
|
|
||||||
modelId: string;
|
|
||||||
}
|
|
||||||
>({
|
|
||||||
element: view,
|
|
||||||
getIsSticky: () => true,
|
|
||||||
canDrop: ({ source }) => {
|
|
||||||
if (source.data.bsEntity?.type === 'blocks') {
|
|
||||||
return (
|
|
||||||
source.data.from?.docId !== widget.doc.id ||
|
|
||||||
source.data.bsEntity.modelIds.every(id => id !== view.model.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
setDropData: () => {
|
|
||||||
return {
|
|
||||||
modelId: view.model.id,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matchFlavours(view.model, ['affine:attachment', 'affine:bookmark'])) {
|
|
||||||
cleanups.push(
|
|
||||||
std.dnd.draggable<DragBlockEntity>({
|
|
||||||
element: view,
|
|
||||||
canDrag: () => {
|
|
||||||
return !isGfxBlockComponent(view);
|
|
||||||
},
|
|
||||||
onDragStart: () => {
|
|
||||||
this.widget.dragging = true;
|
|
||||||
},
|
|
||||||
onDrop: () => {
|
|
||||||
this._cleanup();
|
|
||||||
},
|
|
||||||
setDragPreview: ({ source, container }) => {
|
|
||||||
if (!source.data?.bsEntity?.modelIds.length) {
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
this.previewHelper.renderDragPreview(
|
const widget = this.widget;
|
||||||
source.data?.bsEntity?.modelIds,
|
const std = this.std;
|
||||||
container
|
const disposables = widget.disposables;
|
||||||
);
|
const scrollable = getScrollContainer(this.host);
|
||||||
},
|
|
||||||
setDragData: () => {
|
|
||||||
const { snapshot } = this._getDraggedBlock(view);
|
|
||||||
|
|
||||||
return {
|
if (scrollable) {
|
||||||
type: 'blocks',
|
disposables.add(
|
||||||
modelIds: snapshot ? extractIdsFromSnapshot(snapshot) : [],
|
std.dnd.autoScroll<DragBlockEntity>({
|
||||||
snapshot,
|
element: scrollable,
|
||||||
};
|
canScroll: ({ source }) => {
|
||||||
|
return source.data?.bsEntity?.type === 'blocks';
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dropTargetCleanUps.set(view.model.id, cleanups);
|
disposables.add(this._makeDraggable(this.widget));
|
||||||
};
|
|
||||||
|
// used to handle drag move and drop
|
||||||
|
disposables.add(this._monitorBlockDrag());
|
||||||
|
|
||||||
disposables.add(
|
disposables.add(
|
||||||
std.view.viewUpdated.on(payload => {
|
std.view.viewUpdated.on(payload => {
|
||||||
if (payload.type === 'add') {
|
if (payload.type === 'add') {
|
||||||
makeBlockComponentDropTarget(payload.view);
|
this._makeDropTarget(payload.view);
|
||||||
} else if (
|
} else if (
|
||||||
payload.type === 'delete' &&
|
payload.type === 'delete' &&
|
||||||
dropTargetCleanUps.has(payload.id)
|
this.dropTargetCleanUps.has(payload.id)
|
||||||
) {
|
) {
|
||||||
dropTargetCleanUps.get(payload.id)!.forEach(clean => clean());
|
this.dropTargetCleanUps.get(payload.id)!.forEach(clean => clean());
|
||||||
dropTargetCleanUps.delete(payload.id);
|
this.dropTargetCleanUps.delete(payload.id);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
std.view.views.forEach(block => {
|
std.view.views.forEach(block => this._makeDropTarget(block));
|
||||||
makeBlockComponentDropTarget(block);
|
|
||||||
});
|
|
||||||
|
|
||||||
disposables.add(() => {
|
disposables.add(() => {
|
||||||
dropTargetCleanUps.forEach(cleanUps => cleanUps.forEach(fn => fn()));
|
this.dropTargetCleanUps.forEach(cleanUps => cleanUps.forEach(fn => fn()));
|
||||||
dropTargetCleanUps.clear();
|
this.dropTargetCleanUps.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { effects as componentCaptionEffects } from '@blocksuite/affine-component
|
|||||||
import { effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
|
import { effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
|
||||||
import { effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
|
import { effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
|
||||||
import { effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
|
import { effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
|
||||||
import { effects as componentDragIndicatorEffects } from '@blocksuite/affine-components/drag-indicator';
|
import { effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import { FilterableListComponent } from '@blocksuite/affine-components/filterable-list';
|
import { FilterableListComponent } from '@blocksuite/affine-components/filterable-list';
|
||||||
import { IconButton } from '@blocksuite/affine-components/icon-button';
|
import { IconButton } from '@blocksuite/affine-components/icon-button';
|
||||||
import { effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
|
import { effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
|
||||||
@@ -213,7 +213,7 @@ export function effects() {
|
|||||||
componentPortalEffects();
|
componentPortalEffects();
|
||||||
componentRichTextEffects();
|
componentRichTextEffects();
|
||||||
componentToolbarEffects();
|
componentToolbarEffects();
|
||||||
componentDragIndicatorEffects();
|
componentDropIndicatorEffects();
|
||||||
componentToggleButtonEffects();
|
componentToggleButtonEffects();
|
||||||
componentAiItemEffects();
|
componentAiItemEffects();
|
||||||
componentColorPickerEffects();
|
componentColorPickerEffects();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FileDropExtension } from '@blocksuite/affine-components/drag-indicator';
|
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import {
|
import {
|
||||||
DNDAPIExtension,
|
DNDAPIExtension,
|
||||||
DocModeService,
|
DocModeService,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FileDropExtension } from '@blocksuite/affine-components/drag-indicator';
|
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
|
||||||
import {
|
import {
|
||||||
DNDAPIExtension,
|
DNDAPIExtension,
|
||||||
DocModeService,
|
DocModeService,
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ const chunkGroups = {
|
|||||||
require.resolve('@blocksuite/affine-components/caption'),
|
require.resolve('@blocksuite/affine-components/caption'),
|
||||||
require.resolve('@blocksuite/affine-components/context-menu'),
|
require.resolve('@blocksuite/affine-components/context-menu'),
|
||||||
require.resolve('@blocksuite/affine-components/date-picker'),
|
require.resolve('@blocksuite/affine-components/date-picker'),
|
||||||
require.resolve('@blocksuite/affine-components/drag-indicator'),
|
require.resolve('@blocksuite/affine-components/drop-indicator'),
|
||||||
],
|
],
|
||||||
affine: [
|
affine: [
|
||||||
require.resolve('@blocksuite/affine-shared'),
|
require.resolve('@blocksuite/affine-shared'),
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ test('move drag handle in paragraphs', async ({ page }) => {
|
|||||||
await initThreeParagraphs(page);
|
await initThreeParagraphs(page);
|
||||||
await assertRichTexts(page, ['123', '456', '789']);
|
await assertRichTexts(page, ['123', '456', '789']);
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '2', '4');
|
await dragHandleFromBlockToBlockBottomById(page, '2', '4');
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
await assertRichTexts(page, ['456', '789', '123']);
|
await assertRichTexts(page, ['456', '789', '123']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ test('move drag handle in list', async ({ page }) => {
|
|||||||
await initThreeLists(page);
|
await initThreeLists(page);
|
||||||
await assertRichTexts(page, ['123', '456', '789']);
|
await assertRichTexts(page, ['123', '456', '789']);
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '5', '3', false);
|
await dragHandleFromBlockToBlockBottomById(page, '5', '3', false);
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
await assertRichTexts(page, ['123', '789', '456']);
|
await assertRichTexts(page, ['123', '789', '456']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,11 +106,11 @@ test('move drag handle in nested block', async ({ page }) => {
|
|||||||
await assertRichTexts(page, ['1', '2', '21', '22', '23', '3']);
|
await assertRichTexts(page, ['1', '2', '21', '22', '23', '3']);
|
||||||
|
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '5', '7');
|
await dragHandleFromBlockToBlockBottomById(page, '5', '7');
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
await assertRichTexts(page, ['1', '2', '22', '23', '21', '3']);
|
await assertRichTexts(page, ['1', '2', '22', '23', '21', '3']);
|
||||||
|
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '3', '8');
|
await dragHandleFromBlockToBlockBottomById(page, '3', '8');
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
await assertRichTexts(page, ['2', '22', '23', '21', '3', '1']);
|
await assertRichTexts(page, ['2', '22', '23', '21', '3', '1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ test('move drag handle into another block', async ({ page }) => {
|
|||||||
true,
|
true,
|
||||||
2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
|
2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
|
||||||
);
|
);
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
await assertRichTexts(page, ['1', '2', '22', '23', '21', '3']);
|
await assertRichTexts(page, ['1', '2', '22', '23', '21', '3']);
|
||||||
// FIXME(DND)
|
// FIXME(DND)
|
||||||
// await assertBlockChildrenIds(page, '7', ['5']);
|
// await assertBlockChildrenIds(page, '7', ['5']);
|
||||||
@@ -158,7 +158,7 @@ test('move drag handle into another block', async ({ page }) => {
|
|||||||
// true,
|
// true,
|
||||||
// 2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
|
// 2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
|
||||||
// );
|
// );
|
||||||
// await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
// await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
// await assertRichTexts(page, ['2', '22', '23', '21', '3', '1']);
|
// await assertRichTexts(page, ['2', '22', '23', '21', '3', '1']);
|
||||||
// await assertBlockChildrenIds(page, '8', ['3']);
|
// await assertBlockChildrenIds(page, '8', ['3']);
|
||||||
});
|
});
|
||||||
@@ -193,7 +193,7 @@ test('move to the last block of each level in multi-level nesting', async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '3', '9');
|
await dragHandleFromBlockToBlockBottomById(page, '3', '9');
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
|
|
||||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||||
`${testInfo.title}_drag_3_9.json`
|
`${testInfo.title}_drag_3_9.json`
|
||||||
@@ -206,7 +206,7 @@ test('move to the last block of each level in multi-level nesting', async ({
|
|||||||
true,
|
true,
|
||||||
-(1 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT)
|
-(1 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT)
|
||||||
);
|
);
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
|
|
||||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||||
`${testInfo.title}_drag_4_3.json`
|
`${testInfo.title}_drag_4_3.json`
|
||||||
@@ -220,7 +220,7 @@ test('move to the last block of each level in multi-level nesting', async ({
|
|||||||
true,
|
true,
|
||||||
-(2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT)
|
-(2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT)
|
||||||
);
|
);
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
|
|
||||||
// FIXME(DND)
|
// FIXME(DND)
|
||||||
// expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
// expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||||
@@ -277,7 +277,7 @@ test.fixme(
|
|||||||
await expect(blockSelections).toHaveCount(2);
|
await expect(blockSelections).toHaveCount(2);
|
||||||
|
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '2', '4', true);
|
await dragHandleFromBlockToBlockBottomById(page, '2', '4', true);
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
|
|
||||||
await assertRichTexts(page, ['789', '123', '456']);
|
await assertRichTexts(page, ['789', '123', '456']);
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ test('should blur rich-text first on starting block selection', async ({
|
|||||||
await expect(page.locator('*:focus')).toHaveCount(1);
|
await expect(page.locator('*:focus')).toHaveCount(1);
|
||||||
|
|
||||||
await dragHandleFromBlockToBlockBottomById(page, '2', '4');
|
await dragHandleFromBlockToBlockBottomById(page, '2', '4');
|
||||||
await expect(page.locator('.affine-drag-indicator')).toBeHidden();
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
||||||
await assertRichTexts(page, ['456', '789', '123']);
|
await assertRichTexts(page, ['456', '789', '123']);
|
||||||
|
|
||||||
await expect(page.locator('*:focus')).toHaveCount(0);
|
await expect(page.locator('*:focus')).toHaveCount(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user