chore(core): some dnd events (#9206)

fix AF-1999
This commit is contained in:
pengx17 2024-12-19 07:42:13 +00:00
parent 30588783ef
commit da0f3d0b56
No known key found for this signature in database
GPG Key ID: 23F23D9E8B3971ED
11 changed files with 104 additions and 24 deletions

View File

@ -4,7 +4,11 @@ import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/elem
import { pointerOutsideOfPreview } from '@atlaskit/pragmatic-drag-and-drop/element/pointer-outside-of-preview';
import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
import type { DropTargetRecord } from '@atlaskit/pragmatic-drag-and-drop/types';
import type {
BaseEventPayload,
DropTargetRecord,
ElementDragType,
} from '@atlaskit/pragmatic-drag-and-drop/types';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM, { flushSync } from 'react-dom';
@ -20,6 +24,10 @@ import {
export interface DraggableOptions<D extends DNDData = DNDData> {
data?: DraggableGet<D['draggable']>;
toExternalData?: toExternalData<D>;
onDragStart?: (data: BaseEventPayload<ElementDragType>) => void;
onDrag?: (data: BaseEventPayload<ElementDragType>) => void;
onDrop?: (data: BaseEventPayload<ElementDragType>) => void;
onDropTargetChange?: (data: BaseEventPayload<ElementDragType>) => void;
canDrag?: DraggableGet<boolean>;
disableDragPreview?: boolean;
dragPreviewPosition?: DraggableDragPreviewPosition;
@ -126,8 +134,9 @@ export const useDraggable = <D extends DNDData = DNDData>(
if (dragRef.current) {
dragRef.current.dataset['dragging'] = 'true';
}
options.onDragStart?.(args);
},
onDrop: () => {
onDrop: args => {
if (enableDragging.current) {
setDragging(false);
}
@ -148,6 +157,7 @@ export const useDraggable = <D extends DNDData = DNDData>(
if (dragRef.current) {
delete dragRef.current.dataset['dragging'];
}
options.onDrop?.(args);
},
onDrag: args => {
if (enableDraggingPosition.current) {
@ -163,11 +173,13 @@ export const useDraggable = <D extends DNDData = DNDData>(
outWindow: prev.outWindow,
}));
}
options.onDrag?.(args);
},
onDropTargetChange(args) {
if (enableDropTarget.current) {
setDropTarget(args.location.current.dropTargets);
}
options.onDropTargetChange?.(args);
},
onGenerateDragPreview({ nativeSetDragImage, source, location }) {
if (options.disableDragPreview) {

View File

@ -22,6 +22,7 @@ import { JournalService } from '@affine/core/modules/journal';
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import type { Doc } from '@blocksuite/affine/store';
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
@ -187,6 +188,9 @@ export function DetailPageHeader(
id: page.id,
},
},
onDragStart: () => {
track.$.header.$.dragStart();
},
dragPreviewPosition: 'pointer-outside',
};
}, [page.id]);

View File

@ -219,6 +219,11 @@ const WorkbenchTab = ({
'text/uri-list': urls.join('\n'),
};
},
onDragStart: () => {
track.$.appTabsHeader.$.dragStart({
type: 'tab',
});
},
};
}, [dnd, workbench.basename, workbench.id, workbench.views]);

View File

@ -38,7 +38,15 @@ export class DndService extends Service {
if (source.types.includes(type)) {
const stringData = source.getStringData(type);
if (stringData) {
return resolver(stringData);
const entity = resolver(stringData);
if (entity) {
return {
entity,
from: {
at: 'external',
},
};
}
}
}
return null;
@ -48,7 +56,7 @@ export class DndService extends Service {
private readonly resolvers: ((
source: ExternalDragPayload
) => Entity | null)[] = [];
) => AffineDNDData['draggable'] | null)[] = [];
getBlocksuiteDndAPI(sourceDocId?: string) {
const collection = this.workspaceService.workspace.docCollection;
@ -74,29 +82,23 @@ export class DndService extends Service {
if (!isDropEvent) {
return {};
}
const from: AffineDNDData['draggable']['from'] = {
at: 'external',
};
let entity: Entity | null = null;
let resolved: AffineDNDData['draggable'] | null = null;
// in the order of the resolvers instead of the order of the types
for (const resolver of this.resolvers) {
const candidate = resolver(args.source);
if (candidate) {
entity = candidate;
resolved = candidate;
break;
}
}
if (!entity) {
if (!resolved) {
return {}; // no resolver can handle this data
}
return {
from,
entity,
};
return resolved;
};
toExternalData: toExternalData<AffineDNDData> = (args, data) => {
@ -160,7 +162,7 @@ export class DndService extends Service {
private readonly resolveBlocksuiteExternalData = (
source: ExternalDragPayload
): Entity | null => {
): AffineDNDData['draggable'] | null => {
const dndAPI = this.getBlocksuiteDndAPI();
if (!dndAPI) {
return null;
@ -173,7 +175,16 @@ export class DndService extends Service {
if (!snapshot) {
return null;
}
return this.resolveBlockSnapshot(snapshot);
const entity = this.resolveBlockSnapshot(snapshot);
if (!entity) {
return null;
}
return {
entity,
from: {
at: 'blocksuite-editor',
},
};
};
private readonly resolveHTML: EntityResolver = html => {

View File

@ -125,6 +125,9 @@ export const ExplorerCollectionNode = ({
target: 'doc',
control: 'drag',
});
track.$.navigationPanel.collections.drop({
type: data.source.data.entity.type,
});
}
} else {
onDrop?.(data);

View File

@ -138,6 +138,9 @@ export const ExplorerDocNode = ({
track.$.navigationPanel.docs.linkDoc({
control: 'drag',
});
track.$.navigationPanel.docs.drop({
type: data.source.data.entity.type,
});
} else {
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
}
@ -170,6 +173,9 @@ export const ExplorerDocNode = ({
track.$.navigationPanel.docs.linkDoc({
control: 'drag',
});
track.$.navigationPanel.docs.drop({
type: data.source.data.entity.type,
});
} else {
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
}

View File

@ -241,6 +241,11 @@ const ExplorerFolderNodeFolder = ({
const handleDropOnFolder = useCallback(
(data: DropTargetDropEvent<AffineDNDData>) => {
if (data.source.data.entity?.type) {
track.$.navigationPanel.folders.drop({
type: data.source.data.entity.type,
});
}
if (data.treeInstruction?.type === 'make-child') {
if (data.source.data.entity?.type === 'folder') {
if (
@ -313,6 +318,11 @@ const ExplorerFolderNodeFolder = ({
const handleDropOnPlaceholder = useCallback(
(data: DropTargetDropEvent<AffineDNDData>) => {
if (data.source.data.entity?.type) {
track.$.navigationPanel.folders.drop({
type: data.source.data.entity.type,
});
}
if (data.source.data.entity?.type === 'folder') {
if (
node.id === data.source.data.entity.id ||
@ -353,6 +363,11 @@ const ExplorerFolderNodeFolder = ({
if (!dropAtNode || !dropAtNode.id) {
return;
}
if (data.source.data.entity?.type) {
track.$.navigationPanel.folders.drop({
type: data.source.data.entity.type,
});
}
if (
data.treeInstruction?.type === 'reorder-above' ||
data.treeInstruction?.type === 'reorder-below'

View File

@ -100,6 +100,9 @@ export const ExplorerTagNode = ({
track.$.navigationPanel.tags.tagDoc({
control: 'drag',
});
track.$.navigationPanel.tags.drop({
type: data.source.data.entity.type,
});
} else {
toast(t['com.affine.rootAppSidebar.tag.doc-only']());
}

View File

@ -73,6 +73,9 @@ export const ExplorerFavorites = () => {
type: data.source.data.entity.type,
on: true,
});
track.$.navigationPanel.favorites.drop({
type: data.source.data.entity.type,
});
explorerSection.setCollapsed(false);
}
},
@ -141,6 +144,9 @@ export const ExplorerFavorites = () => {
type: data.source.data.entity.type,
on: true,
});
track.$.navigationPanel.favorites.drop({
type: data.source.data.entity.type,
});
} else {
return; // not supported
}

View File

@ -80,7 +80,10 @@ export interface AffineDNDData extends DNDData {
docId: string;
}
| {
at: 'external'; // for blocksuite or external apps
at: 'blocksuite-editor';
}
| {
at: 'external'; // for external apps
};
};
dropTarget:

View File

@ -86,6 +86,8 @@ type OrganizeEvents =
| FolderEvents
| TagEvents
| FavoriteEvents;
type DNDEvents = 'dragStart' | 'drag' | 'drop';
// END SECTION
// SECTION: cloud events
@ -127,7 +129,8 @@ type UserEvents =
| ShareEvents
| AuthEvents
| AccountEvents
| PaymentEvents;
| PaymentEvents
| DNDEvents;
interface PageDivision {
[page: string]: {
[segment: string]: {
@ -209,12 +212,18 @@ const PageEvents = {
'openInNewTab',
'openInSplitView',
'toggleFavorite',
'drop',
],
docs: ['createDoc', 'deleteDoc', 'linkDoc'],
collections: ['createDoc', 'addDocToCollection', 'removeOrganizeItem'],
folders: ['createDoc'],
tags: ['createDoc', 'tagDoc'],
favorites: ['createDoc'],
docs: ['createDoc', 'deleteDoc', 'linkDoc', 'drop'],
collections: [
'createDoc',
'addDocToCollection',
'removeOrganizeItem',
'drop',
],
folders: ['createDoc', 'drop'],
tags: ['createDoc', 'tagDoc', 'drop'],
favorites: ['createDoc', 'drop'],
migrationData: ['openMigrationDataHelp'],
bottomButtons: [
'downloadApp',
@ -248,9 +257,10 @@ const PageEvents = {
aiAction: ['viewPlans'],
},
appTabsHeader: {
$: ['tabAction'],
$: ['tabAction', 'dragStart'],
},
header: {
$: ['dragStart'],
actions: [
'createDoc',
'createWorkspace',
@ -423,6 +433,8 @@ export type EventArgs = {
editProperty: { type: string };
addProperty: { type: string; control: 'at menu' | 'property list' };
linkDoc: { type: string; journal: boolean };
drop: { type: string };
dragStart: { type: string };
};
// for type checking