Skip to content

Commit 58ecd85

Browse files
committed
feat(core): add new bs dnd adapter
1 parent 9971719 commit 58ecd85

File tree

8 files changed

+122
-10
lines changed

8 files changed

+122
-10
lines changed

blocksuite/affine/shared/src/services/drag-handle-config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { type BlockStdScope, StdIdentifier } from '@blocksuite/block-std';
22
import { type Container, createIdentifier } from '@blocksuite/global/di';
33
import { Extension, Slice, type SliceSnapshot } from '@blocksuite/store';
44

5+
export type { DragBlockPayload } from '@blocksuite/affine-widget-drag-handle';
6+
57
export const DndApiExtensionIdentifier = createIdentifier<DNDAPIExtension>(
68
'AffineDndApiIdentifier'
79
);

blocksuite/affine/widget-drag-handle/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ declare type _GLOBAL_ = typeof SurfaceEffects;
55
export * from './consts';
66
export * from './drag-handle';
77
export * from './utils';
8+
export type { DragBlockPayload } from './watchers/drag-event-watcher';

packages/frontend/component/src/ui/dnd/monitor.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { getAdaptedEventArgs } from './common';
1010
import { DNDContext } from './context';
1111
import type { DNDData, fromExternalData } from './types';
1212

13-
type MonitorGetFeedback<D extends DNDData = DNDData> = Parameters<
13+
export type MonitorGetFeedback<D extends DNDData = DNDData> = Parameters<
1414
NonNullable<Parameters<typeof monitorForElements>[0]['canMonitor']>
1515
>[0] & {
1616
source: {
@@ -22,7 +22,7 @@ type MonitorGet<T, D extends DNDData = DNDData> =
2222
| T
2323
| ((data: MonitorGetFeedback<D>) => T);
2424

25-
type MonitorDragEvent<D extends DNDData = DNDData> = {
25+
export type MonitorDragEvent<D extends DNDData = DNDData> = {
2626
/**
2727
* Location history for the drag operation
2828
*/
@@ -119,3 +119,5 @@ export const useDndMonitor = <D extends DNDData = DNDData>(
119119
return monitorForExternal(monitorOptions);
120120
}, [monitorOptions, options.fromExternalData]);
121121
};
122+
123+
export { monitorForElements };

packages/frontend/core/src/mobile/components/explorer/tree/node.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,11 @@ export const ExplorerTreeNode = ({
180180
>
181181
<div className={styles.contentContainer} data-open={!collapsed}>
182182
{to ? (
183-
<LinkComponent to={to} className={styles.linkItemRoot}>
183+
<LinkComponent
184+
to={to}
185+
className={styles.linkItemRoot}
186+
draggable={false}
187+
>
184188
{content}
185189
</LinkComponent>
186190
) : (

packages/frontend/core/src/modules/dnd/services/index.ts

+97-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import {
22
type ExternalGetDataFeedbackArgs,
33
type fromExternalData,
4+
monitorForElements,
5+
type MonitorGetFeedback,
46
type toExternalData,
57
} from '@affine/component';
68
import { createPageModeSpecs } from '@affine/core/components/blocksuite/block-suite-editor/specs/page';
79
import type { AffineDNDData } from '@affine/core/types/dnd';
810
import { BlockStdScope } from '@blocksuite/affine/block-std';
9-
import { DndApiExtensionIdentifier } from '@blocksuite/affine/blocks';
11+
import {
12+
DndApiExtensionIdentifier,
13+
type DragBlockPayload,
14+
} from '@blocksuite/affine/blocks';
1015
import { type SliceSnapshot } from '@blocksuite/affine/store';
1116
import { Service } from '@toeverything/infra';
1217

@@ -19,6 +24,10 @@ type EntityResolver = (data: string) => Entity | null;
1924

2025
type ExternalDragPayload = ExternalGetDataFeedbackArgs['source'];
2126

27+
type MixedDNDData = AffineDNDData & {
28+
draggable: DragBlockPayload;
29+
};
30+
2231
export class DndService extends Service {
2332
constructor(
2433
private readonly docsService: DocsService,
@@ -53,6 +62,90 @@ export class DndService extends Service {
5362
return null;
5463
});
5564
});
65+
66+
this.setupBlocksuiteAdapter();
67+
}
68+
69+
private setupBlocksuiteAdapter() {
70+
/**
71+
* Migrate from affine to blocksuite
72+
* For now, we only support doc
73+
*/
74+
const affineToBlocksuite = (args: MonitorGetFeedback<MixedDNDData>) => {
75+
const data = args.source.data;
76+
if (data.entity && !data.bsEntity) {
77+
if (data.entity.type !== 'doc') {
78+
return;
79+
}
80+
const dndAPI = this.getBlocksuiteDndAPI();
81+
if (!dndAPI) {
82+
return;
83+
}
84+
const snapshotSlice = dndAPI.fromEntity({
85+
docId: data.entity.id,
86+
flavour: 'affine:embed-linked-doc',
87+
});
88+
if (!snapshotSlice) {
89+
return;
90+
}
91+
data.bsEntity = {
92+
type: 'blocks',
93+
modelIds: [],
94+
snapshot: snapshotSlice,
95+
};
96+
}
97+
};
98+
99+
/**
100+
* Migrate from blocksuite to affine
101+
*/
102+
const blocksuiteToAffine = (args: MonitorGetFeedback<MixedDNDData>) => {
103+
const data = args.source.data;
104+
if (!data.entity && data.bsEntity) {
105+
if (data.bsEntity.type !== 'blocks' || !data.bsEntity.snapshot) {
106+
return;
107+
}
108+
const dndAPI = this.getBlocksuiteDndAPI();
109+
if (!dndAPI) {
110+
return;
111+
}
112+
const entity = this.resolveBlockSnapshot(data.bsEntity.snapshot);
113+
if (!entity) {
114+
return;
115+
}
116+
data.entity = entity;
117+
}
118+
};
119+
120+
function adaptDragEvent(args: MonitorGetFeedback<MixedDNDData>) {
121+
affineToBlocksuite(args);
122+
blocksuiteToAffine(args);
123+
}
124+
125+
function canMonitor(args: MonitorGetFeedback<MixedDNDData>) {
126+
return (
127+
args.source.data.entity?.type === 'doc' ||
128+
(args.source.data.bsEntity?.type === 'blocks' &&
129+
!!args.source.data.bsEntity.snapshot)
130+
);
131+
}
132+
133+
this.disposables.push(
134+
monitorForElements({
135+
canMonitor: (args: MonitorGetFeedback<MixedDNDData>) => {
136+
if (canMonitor(args)) {
137+
// HACK ahead:
138+
// canMonitor shall be used a pure function, which means
139+
// we may need to adapt the drag event to make sure the data is applied onDragStart.
140+
// However, canMonitor in blocksuite is also called BEFORE onDragStart,
141+
// so we need to adapt it here in onMonitor
142+
adaptDragEvent(args);
143+
return true;
144+
}
145+
return false;
146+
},
147+
})
148+
);
56149
}
57150

58151
private readonly resolvers: ((
@@ -161,6 +254,9 @@ export class DndService extends Service {
161254
return null;
162255
};
163256

257+
/**
258+
* @deprecated Blocksuite DND is now using pragmatic-dnd as well
259+
*/
164260
private readonly resolveBlocksuiteExternalData = (
165261
source: ExternalDragPayload
166262
): AffineDNDData['draggable'] | null => {

packages/frontend/core/src/modules/explorer/views/tree/node.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ export interface BaseExplorerTreeNodeProps {
7272
childrenOperations?: NodeOperation[];
7373
childrenPlaceholder?: React.ReactNode;
7474
linkComponent?: React.ComponentType<
75-
React.PropsWithChildren<{ to: To; className?: string }> & RefAttributes<any>
75+
React.PropsWithChildren<{ to: To; className?: string }> &
76+
RefAttributes<any> & { draggable?: boolean }
7677
>;
7778
[key: `data-${string}`]: any;
7879
}
@@ -433,7 +434,12 @@ export const ExplorerTreeNode = ({
433434
ref={dropTargetRef}
434435
>
435436
{to ? (
436-
<LinkComponent to={to} className={styles.linkItemRoot} ref={dragRef}>
437+
<LinkComponent
438+
to={to}
439+
className={styles.linkItemRoot}
440+
ref={dragRef}
441+
draggable={false}
442+
>
437443
{content}
438444
</LinkComponent>
439445
) : (

packages/frontend/core/src/modules/workbench/view/split-view/split-view.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ export const SplitView = ({
8585

8686
useDndMonitor<AffineDNDData>(() => {
8787
return {
88-
// todo(@pengx17): external data for monitor is not supported yet
89-
// allowExternal: true,
9088
canMonitor(data) {
89+
if (!BUILD_CONFIG.isElectron) {
90+
return false;
91+
}
9192
// allow dropping doc && tab view to split view panel
9293
const from = data.source.data.from;
9394
const entity = data.source.data.entity;

tests/affine-local/e2e/drag-page.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ test('drag a page link in editor to favourites', async ({ page }) => {
243243
);
244244
});
245245

246-
test.skip('drag a page card block to another page', async ({ page }) => {
246+
test('drag a page card block to another page', async ({ page }) => {
247247
await clickNewPageButton(page);
248248
await page.waitForTimeout(500);
249249
await page.keyboard.press('Enter');
@@ -293,7 +293,7 @@ test.skip('drag a page card block to another page', async ({ page }) => {
293293
);
294294
});
295295

296-
test.skip('drag a favourite page into blocksuite', async ({ page }) => {
296+
test('drag a favourite page into blocksuite', async ({ page }) => {
297297
await clickNewPageButton(page, 'hi from page');
298298
await page.getByTestId('pin-button').click();
299299
const pageId = getCurrentDocIdFromUrl(page);

0 commit comments

Comments
 (0)