mirror of
https://github.com/toeverything/AFFiNE.git
synced 2025-01-02 06:21:34 +03:00
parent
30588783ef
commit
da0f3d0b56
@ -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 { 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 { 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 { 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 { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import ReactDOM, { flushSync } from 'react-dom';
|
import ReactDOM, { flushSync } from 'react-dom';
|
||||||
|
|
||||||
@ -20,6 +24,10 @@ import {
|
|||||||
export interface DraggableOptions<D extends DNDData = DNDData> {
|
export interface DraggableOptions<D extends DNDData = DNDData> {
|
||||||
data?: DraggableGet<D['draggable']>;
|
data?: DraggableGet<D['draggable']>;
|
||||||
toExternalData?: toExternalData<D>;
|
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>;
|
canDrag?: DraggableGet<boolean>;
|
||||||
disableDragPreview?: boolean;
|
disableDragPreview?: boolean;
|
||||||
dragPreviewPosition?: DraggableDragPreviewPosition;
|
dragPreviewPosition?: DraggableDragPreviewPosition;
|
||||||
@ -126,8 +134,9 @@ export const useDraggable = <D extends DNDData = DNDData>(
|
|||||||
if (dragRef.current) {
|
if (dragRef.current) {
|
||||||
dragRef.current.dataset['dragging'] = 'true';
|
dragRef.current.dataset['dragging'] = 'true';
|
||||||
}
|
}
|
||||||
|
options.onDragStart?.(args);
|
||||||
},
|
},
|
||||||
onDrop: () => {
|
onDrop: args => {
|
||||||
if (enableDragging.current) {
|
if (enableDragging.current) {
|
||||||
setDragging(false);
|
setDragging(false);
|
||||||
}
|
}
|
||||||
@ -148,6 +157,7 @@ export const useDraggable = <D extends DNDData = DNDData>(
|
|||||||
if (dragRef.current) {
|
if (dragRef.current) {
|
||||||
delete dragRef.current.dataset['dragging'];
|
delete dragRef.current.dataset['dragging'];
|
||||||
}
|
}
|
||||||
|
options.onDrop?.(args);
|
||||||
},
|
},
|
||||||
onDrag: args => {
|
onDrag: args => {
|
||||||
if (enableDraggingPosition.current) {
|
if (enableDraggingPosition.current) {
|
||||||
@ -163,11 +173,13 @@ export const useDraggable = <D extends DNDData = DNDData>(
|
|||||||
outWindow: prev.outWindow,
|
outWindow: prev.outWindow,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
options.onDrag?.(args);
|
||||||
},
|
},
|
||||||
onDropTargetChange(args) {
|
onDropTargetChange(args) {
|
||||||
if (enableDropTarget.current) {
|
if (enableDropTarget.current) {
|
||||||
setDropTarget(args.location.current.dropTargets);
|
setDropTarget(args.location.current.dropTargets);
|
||||||
}
|
}
|
||||||
|
options.onDropTargetChange?.(args);
|
||||||
},
|
},
|
||||||
onGenerateDragPreview({ nativeSetDragImage, source, location }) {
|
onGenerateDragPreview({ nativeSetDragImage, source, location }) {
|
||||||
if (options.disableDragPreview) {
|
if (options.disableDragPreview) {
|
||||||
|
@ -22,6 +22,7 @@ import { JournalService } from '@affine/core/modules/journal';
|
|||||||
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import { track } from '@affine/track';
|
||||||
import type { Doc } from '@blocksuite/affine/store';
|
import type { Doc } from '@blocksuite/affine/store';
|
||||||
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
||||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
@ -187,6 +188,9 @@ export function DetailPageHeader(
|
|||||||
id: page.id,
|
id: page.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
onDragStart: () => {
|
||||||
|
track.$.header.$.dragStart();
|
||||||
|
},
|
||||||
dragPreviewPosition: 'pointer-outside',
|
dragPreviewPosition: 'pointer-outside',
|
||||||
};
|
};
|
||||||
}, [page.id]);
|
}, [page.id]);
|
||||||
|
@ -219,6 +219,11 @@ const WorkbenchTab = ({
|
|||||||
'text/uri-list': urls.join('\n'),
|
'text/uri-list': urls.join('\n'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
onDragStart: () => {
|
||||||
|
track.$.appTabsHeader.$.dragStart({
|
||||||
|
type: 'tab',
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}, [dnd, workbench.basename, workbench.id, workbench.views]);
|
}, [dnd, workbench.basename, workbench.id, workbench.views]);
|
||||||
|
|
||||||
|
@ -38,7 +38,15 @@ export class DndService extends Service {
|
|||||||
if (source.types.includes(type)) {
|
if (source.types.includes(type)) {
|
||||||
const stringData = source.getStringData(type);
|
const stringData = source.getStringData(type);
|
||||||
if (stringData) {
|
if (stringData) {
|
||||||
return resolver(stringData);
|
const entity = resolver(stringData);
|
||||||
|
if (entity) {
|
||||||
|
return {
|
||||||
|
entity,
|
||||||
|
from: {
|
||||||
|
at: 'external',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -48,7 +56,7 @@ export class DndService extends Service {
|
|||||||
|
|
||||||
private readonly resolvers: ((
|
private readonly resolvers: ((
|
||||||
source: ExternalDragPayload
|
source: ExternalDragPayload
|
||||||
) => Entity | null)[] = [];
|
) => AffineDNDData['draggable'] | null)[] = [];
|
||||||
|
|
||||||
getBlocksuiteDndAPI(sourceDocId?: string) {
|
getBlocksuiteDndAPI(sourceDocId?: string) {
|
||||||
const collection = this.workspaceService.workspace.docCollection;
|
const collection = this.workspaceService.workspace.docCollection;
|
||||||
@ -74,29 +82,23 @@ export class DndService extends Service {
|
|||||||
if (!isDropEvent) {
|
if (!isDropEvent) {
|
||||||
return {};
|
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
|
// in the order of the resolvers instead of the order of the types
|
||||||
for (const resolver of this.resolvers) {
|
for (const resolver of this.resolvers) {
|
||||||
const candidate = resolver(args.source);
|
const candidate = resolver(args.source);
|
||||||
if (candidate) {
|
if (candidate) {
|
||||||
entity = candidate;
|
resolved = candidate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entity) {
|
if (!resolved) {
|
||||||
return {}; // no resolver can handle this data
|
return {}; // no resolver can handle this data
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return resolved;
|
||||||
from,
|
|
||||||
entity,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
toExternalData: toExternalData<AffineDNDData> = (args, data) => {
|
toExternalData: toExternalData<AffineDNDData> = (args, data) => {
|
||||||
@ -160,7 +162,7 @@ export class DndService extends Service {
|
|||||||
|
|
||||||
private readonly resolveBlocksuiteExternalData = (
|
private readonly resolveBlocksuiteExternalData = (
|
||||||
source: ExternalDragPayload
|
source: ExternalDragPayload
|
||||||
): Entity | null => {
|
): AffineDNDData['draggable'] | null => {
|
||||||
const dndAPI = this.getBlocksuiteDndAPI();
|
const dndAPI = this.getBlocksuiteDndAPI();
|
||||||
if (!dndAPI) {
|
if (!dndAPI) {
|
||||||
return null;
|
return null;
|
||||||
@ -173,7 +175,16 @@ export class DndService extends Service {
|
|||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
return null;
|
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 => {
|
private readonly resolveHTML: EntityResolver = html => {
|
||||||
|
@ -125,6 +125,9 @@ export const ExplorerCollectionNode = ({
|
|||||||
target: 'doc',
|
target: 'doc',
|
||||||
control: 'drag',
|
control: 'drag',
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.collections.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onDrop?.(data);
|
onDrop?.(data);
|
||||||
|
@ -138,6 +138,9 @@ export const ExplorerDocNode = ({
|
|||||||
track.$.navigationPanel.docs.linkDoc({
|
track.$.navigationPanel.docs.linkDoc({
|
||||||
control: 'drag',
|
control: 'drag',
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.docs.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
||||||
}
|
}
|
||||||
@ -170,6 +173,9 @@ export const ExplorerDocNode = ({
|
|||||||
track.$.navigationPanel.docs.linkDoc({
|
track.$.navigationPanel.docs.linkDoc({
|
||||||
control: 'drag',
|
control: 'drag',
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.docs.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
||||||
}
|
}
|
||||||
|
@ -241,6 +241,11 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
|
|
||||||
const handleDropOnFolder = useCallback(
|
const handleDropOnFolder = useCallback(
|
||||||
(data: DropTargetDropEvent<AffineDNDData>) => {
|
(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.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'folder') {
|
if (data.source.data.entity?.type === 'folder') {
|
||||||
if (
|
if (
|
||||||
@ -313,6 +318,11 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
|
|
||||||
const handleDropOnPlaceholder = useCallback(
|
const handleDropOnPlaceholder = useCallback(
|
||||||
(data: DropTargetDropEvent<AffineDNDData>) => {
|
(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 (data.source.data.entity?.type === 'folder') {
|
||||||
if (
|
if (
|
||||||
node.id === data.source.data.entity.id ||
|
node.id === data.source.data.entity.id ||
|
||||||
@ -353,6 +363,11 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
if (!dropAtNode || !dropAtNode.id) {
|
if (!dropAtNode || !dropAtNode.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (data.source.data.entity?.type) {
|
||||||
|
track.$.navigationPanel.folders.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
data.treeInstruction?.type === 'reorder-above' ||
|
data.treeInstruction?.type === 'reorder-above' ||
|
||||||
data.treeInstruction?.type === 'reorder-below'
|
data.treeInstruction?.type === 'reorder-below'
|
||||||
|
@ -100,6 +100,9 @@ export const ExplorerTagNode = ({
|
|||||||
track.$.navigationPanel.tags.tagDoc({
|
track.$.navigationPanel.tags.tagDoc({
|
||||||
control: 'drag',
|
control: 'drag',
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.tags.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.tag.doc-only']());
|
toast(t['com.affine.rootAppSidebar.tag.doc-only']());
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,9 @@ export const ExplorerFavorites = () => {
|
|||||||
type: data.source.data.entity.type,
|
type: data.source.data.entity.type,
|
||||||
on: true,
|
on: true,
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.favorites.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
explorerSection.setCollapsed(false);
|
explorerSection.setCollapsed(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -141,6 +144,9 @@ export const ExplorerFavorites = () => {
|
|||||||
type: data.source.data.entity.type,
|
type: data.source.data.entity.type,
|
||||||
on: true,
|
on: true,
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.favorites.drop({
|
||||||
|
type: data.source.data.entity.type,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return; // not supported
|
return; // not supported
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,10 @@ export interface AffineDNDData extends DNDData {
|
|||||||
docId: string;
|
docId: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
at: 'external'; // for blocksuite or external apps
|
at: 'blocksuite-editor';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
at: 'external'; // for external apps
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
dropTarget:
|
dropTarget:
|
||||||
|
@ -86,6 +86,8 @@ type OrganizeEvents =
|
|||||||
| FolderEvents
|
| FolderEvents
|
||||||
| TagEvents
|
| TagEvents
|
||||||
| FavoriteEvents;
|
| FavoriteEvents;
|
||||||
|
|
||||||
|
type DNDEvents = 'dragStart' | 'drag' | 'drop';
|
||||||
// END SECTION
|
// END SECTION
|
||||||
|
|
||||||
// SECTION: cloud events
|
// SECTION: cloud events
|
||||||
@ -127,7 +129,8 @@ type UserEvents =
|
|||||||
| ShareEvents
|
| ShareEvents
|
||||||
| AuthEvents
|
| AuthEvents
|
||||||
| AccountEvents
|
| AccountEvents
|
||||||
| PaymentEvents;
|
| PaymentEvents
|
||||||
|
| DNDEvents;
|
||||||
interface PageDivision {
|
interface PageDivision {
|
||||||
[page: string]: {
|
[page: string]: {
|
||||||
[segment: string]: {
|
[segment: string]: {
|
||||||
@ -209,12 +212,18 @@ const PageEvents = {
|
|||||||
'openInNewTab',
|
'openInNewTab',
|
||||||
'openInSplitView',
|
'openInSplitView',
|
||||||
'toggleFavorite',
|
'toggleFavorite',
|
||||||
|
'drop',
|
||||||
],
|
],
|
||||||
docs: ['createDoc', 'deleteDoc', 'linkDoc'],
|
docs: ['createDoc', 'deleteDoc', 'linkDoc', 'drop'],
|
||||||
collections: ['createDoc', 'addDocToCollection', 'removeOrganizeItem'],
|
collections: [
|
||||||
folders: ['createDoc'],
|
'createDoc',
|
||||||
tags: ['createDoc', 'tagDoc'],
|
'addDocToCollection',
|
||||||
favorites: ['createDoc'],
|
'removeOrganizeItem',
|
||||||
|
'drop',
|
||||||
|
],
|
||||||
|
folders: ['createDoc', 'drop'],
|
||||||
|
tags: ['createDoc', 'tagDoc', 'drop'],
|
||||||
|
favorites: ['createDoc', 'drop'],
|
||||||
migrationData: ['openMigrationDataHelp'],
|
migrationData: ['openMigrationDataHelp'],
|
||||||
bottomButtons: [
|
bottomButtons: [
|
||||||
'downloadApp',
|
'downloadApp',
|
||||||
@ -248,9 +257,10 @@ const PageEvents = {
|
|||||||
aiAction: ['viewPlans'],
|
aiAction: ['viewPlans'],
|
||||||
},
|
},
|
||||||
appTabsHeader: {
|
appTabsHeader: {
|
||||||
$: ['tabAction'],
|
$: ['tabAction', 'dragStart'],
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
|
$: ['dragStart'],
|
||||||
actions: [
|
actions: [
|
||||||
'createDoc',
|
'createDoc',
|
||||||
'createWorkspace',
|
'createWorkspace',
|
||||||
@ -423,6 +433,8 @@ export type EventArgs = {
|
|||||||
editProperty: { type: string };
|
editProperty: { type: string };
|
||||||
addProperty: { type: string; control: 'at menu' | 'property list' };
|
addProperty: { type: string; control: 'at menu' | 'property list' };
|
||||||
linkDoc: { type: string; journal: boolean };
|
linkDoc: { type: string; journal: boolean };
|
||||||
|
drop: { type: string };
|
||||||
|
dragStart: { type: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
// for type checking
|
// for type checking
|
||||||
|
Loading…
Reference in New Issue
Block a user