mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 05:14:54 +00:00
feat(core): add new bs dnd adapter (#9717)
This commit is contained in:
@@ -180,7 +180,11 @@ export const ExplorerTreeNode = ({
|
||||
>
|
||||
<div className={styles.contentContainer} data-open={!collapsed}>
|
||||
{to ? (
|
||||
<LinkComponent to={to} className={styles.linkItemRoot}>
|
||||
<LinkComponent
|
||||
to={to}
|
||||
className={styles.linkItemRoot}
|
||||
draggable={false}
|
||||
>
|
||||
{content}
|
||||
</LinkComponent>
|
||||
) : (
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import {
|
||||
type ExternalGetDataFeedbackArgs,
|
||||
type fromExternalData,
|
||||
monitorForElements,
|
||||
type MonitorGetFeedback,
|
||||
type toExternalData,
|
||||
} from '@affine/component';
|
||||
import { createPageModeSpecs } from '@affine/core/components/blocksuite/block-suite-editor/specs/page';
|
||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||
import { BlockStdScope } from '@blocksuite/affine/block-std';
|
||||
import { DndApiExtensionIdentifier } from '@blocksuite/affine/blocks';
|
||||
import {
|
||||
DndApiExtensionIdentifier,
|
||||
type DragBlockPayload,
|
||||
} from '@blocksuite/affine/blocks';
|
||||
import { type SliceSnapshot } from '@blocksuite/affine/store';
|
||||
import { Service } from '@toeverything/infra';
|
||||
|
||||
@@ -19,6 +24,10 @@ type EntityResolver = (data: string) => Entity | null;
|
||||
|
||||
type ExternalDragPayload = ExternalGetDataFeedbackArgs['source'];
|
||||
|
||||
type MixedDNDData = AffineDNDData & {
|
||||
draggable: DragBlockPayload;
|
||||
};
|
||||
|
||||
export class DndService extends Service {
|
||||
constructor(
|
||||
private readonly docsService: DocsService,
|
||||
@@ -53,6 +62,90 @@ export class DndService extends Service {
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
this.setupBlocksuiteAdapter();
|
||||
}
|
||||
|
||||
private setupBlocksuiteAdapter() {
|
||||
/**
|
||||
* Migrate from affine to blocksuite
|
||||
* For now, we only support doc
|
||||
*/
|
||||
const affineToBlocksuite = (args: MonitorGetFeedback<MixedDNDData>) => {
|
||||
const data = args.source.data;
|
||||
if (data.entity && !data.bsEntity) {
|
||||
if (data.entity.type !== 'doc') {
|
||||
return;
|
||||
}
|
||||
const dndAPI = this.getBlocksuiteDndAPI();
|
||||
if (!dndAPI) {
|
||||
return;
|
||||
}
|
||||
const snapshotSlice = dndAPI.fromEntity({
|
||||
docId: data.entity.id,
|
||||
flavour: 'affine:embed-linked-doc',
|
||||
});
|
||||
if (!snapshotSlice) {
|
||||
return;
|
||||
}
|
||||
data.bsEntity = {
|
||||
type: 'blocks',
|
||||
modelIds: [],
|
||||
snapshot: snapshotSlice,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Migrate from blocksuite to affine
|
||||
*/
|
||||
const blocksuiteToAffine = (args: MonitorGetFeedback<MixedDNDData>) => {
|
||||
const data = args.source.data;
|
||||
if (!data.entity && data.bsEntity) {
|
||||
if (data.bsEntity.type !== 'blocks' || !data.bsEntity.snapshot) {
|
||||
return;
|
||||
}
|
||||
const dndAPI = this.getBlocksuiteDndAPI();
|
||||
if (!dndAPI) {
|
||||
return;
|
||||
}
|
||||
const entity = this.resolveBlockSnapshot(data.bsEntity.snapshot);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
data.entity = entity;
|
||||
}
|
||||
};
|
||||
|
||||
function adaptDragEvent(args: MonitorGetFeedback<MixedDNDData>) {
|
||||
affineToBlocksuite(args);
|
||||
blocksuiteToAffine(args);
|
||||
}
|
||||
|
||||
function canMonitor(args: MonitorGetFeedback<MixedDNDData>) {
|
||||
return (
|
||||
args.source.data.entity?.type === 'doc' ||
|
||||
(args.source.data.bsEntity?.type === 'blocks' &&
|
||||
!!args.source.data.bsEntity.snapshot)
|
||||
);
|
||||
}
|
||||
|
||||
this.disposables.push(
|
||||
monitorForElements({
|
||||
canMonitor: (args: MonitorGetFeedback<MixedDNDData>) => {
|
||||
if (canMonitor(args)) {
|
||||
// HACK ahead:
|
||||
// canMonitor shall be used a pure function, which means
|
||||
// we may need to adapt the drag event to make sure the data is applied onDragStart.
|
||||
// However, canMonitor in blocksuite is also called BEFORE onDragStart,
|
||||
// so we need to adapt it here in onMonitor
|
||||
adaptDragEvent(args);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private readonly resolvers: ((
|
||||
@@ -161,6 +254,9 @@ export class DndService extends Service {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Blocksuite DND is now using pragmatic-dnd as well
|
||||
*/
|
||||
private readonly resolveBlocksuiteExternalData = (
|
||||
source: ExternalDragPayload
|
||||
): AffineDNDData['draggable'] | null => {
|
||||
|
||||
@@ -72,7 +72,8 @@ export interface BaseExplorerTreeNodeProps {
|
||||
childrenOperations?: NodeOperation[];
|
||||
childrenPlaceholder?: React.ReactNode;
|
||||
linkComponent?: React.ComponentType<
|
||||
React.PropsWithChildren<{ to: To; className?: string }> & RefAttributes<any>
|
||||
React.PropsWithChildren<{ to: To; className?: string }> &
|
||||
RefAttributes<any> & { draggable?: boolean }
|
||||
>;
|
||||
[key: `data-${string}`]: any;
|
||||
}
|
||||
@@ -433,7 +434,12 @@ export const ExplorerTreeNode = ({
|
||||
ref={dropTargetRef}
|
||||
>
|
||||
{to ? (
|
||||
<LinkComponent to={to} className={styles.linkItemRoot} ref={dragRef}>
|
||||
<LinkComponent
|
||||
to={to}
|
||||
className={styles.linkItemRoot}
|
||||
ref={dragRef}
|
||||
draggable={false}
|
||||
>
|
||||
{content}
|
||||
</LinkComponent>
|
||||
) : (
|
||||
|
||||
@@ -85,9 +85,10 @@ export const SplitView = ({
|
||||
|
||||
useDndMonitor<AffineDNDData>(() => {
|
||||
return {
|
||||
// todo(@pengx17): external data for monitor is not supported yet
|
||||
// allowExternal: true,
|
||||
canMonitor(data) {
|
||||
if (!BUILD_CONFIG.isElectron) {
|
||||
return false;
|
||||
}
|
||||
// allow dropping doc && tab view to split view panel
|
||||
const from = data.source.data.from;
|
||||
const entity = data.source.data.entity;
|
||||
|
||||
Reference in New Issue
Block a user