feat(core): allow bs snapshot dragging targets (#9093)

fix AF-1924, AF-1848, AF-1928, AF-1931

dnd between affine & editor

<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/T2klNLEk0wxLh4NRDzhk/dff3ceb1-dc82-4222-9b55-13be80b28b2f.mp4">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/T2klNLEk0wxLh4NRDzhk/dff3ceb1-dc82-4222-9b55-13be80b28b2f.mp4">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/dff3ceb1-dc82-4222-9b55-13be80b28b2f.mp4">20241210-1217-49.8960381.mp4</video>
This commit is contained in:
pengx17
2024-12-11 08:12:01 +00:00
parent 331e674e8b
commit dc7d128252
13 changed files with 457 additions and 141 deletions
@@ -5,42 +5,21 @@ import { pointerOutsideOfPreview } from '@atlaskit/pragmatic-drag-and-drop/eleme
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 { useEffect, useMemo, useRef, useState } from 'react';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM, { flushSync } from 'react-dom';
import type { DNDData } from './types';
type DraggableGetFeedback = Parameters<
NonNullable<Parameters<typeof draggable>[0]['getInitialData']>
>[0];
type DraggableGet<T> = T | ((data: DraggableGetFeedback) => T);
function draggableGet<T>(
get: T
): T extends undefined
? undefined
: T extends DraggableGet<infer I>
? (args: DraggableGetFeedback) => I
: never {
if (get === undefined) {
return undefined as any;
}
return ((args: DraggableGetFeedback) =>
typeof get === 'function' ? (get as any)(args) : get) as any;
}
import { DNDContext } from './context';
import {
type DNDData,
type DraggableGet,
draggableGet,
type DraggableGetFeedback,
type toExternalData,
} from './types';
export interface DraggableOptions<D extends DNDData = DNDData> {
data?: DraggableGet<D['draggable']>;
dataForExternal?: DraggableGet<{
[Key in
| 'text/uri-list'
| 'text/plain'
| 'text/html'
| 'Files'
// eslint-disable-next-line @typescript-eslint/ban-types
| (string & {})]?: string;
}>;
toExternalData?: toExternalData<D>;
canDrag?: DraggableGet<boolean>;
disableDragPreview?: boolean;
dragPreviewPosition?: DraggableDragPreviewPosition;
@@ -82,8 +61,23 @@ export const useDraggable = <D extends DNDData = DNDData>(
const enableDropTarget = useRef(false);
const enableDragging = useRef(false);
const context = useContext(DNDContext);
// eslint-disable-next-line react-hooks/exhaustive-deps
const options = useMemo(getOptions, deps);
const options = useMemo(() => {
const opts = getOptions();
const toExternalData = opts.toExternalData ?? context.toExternalData;
return {
...opts,
toExternalData: toExternalData
? (args: DraggableGetFeedback) => {
return (opts.toExternalData ?? toExternalData)(args, opts.data);
}
: undefined,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...deps, context.toExternalData]);
useEffect(() => {
if (!dragRef.current) {
@@ -110,7 +104,7 @@ export const useDraggable = <D extends DNDData = DNDData>(
dragHandle: dragHandleRef.current ?? undefined,
canDrag: draggableGet(options.canDrag),
getInitialData: draggableGet(options.data),
getInitialDataForExternal: draggableGet(options.dataForExternal),
getInitialDataForExternal: draggableGet(options.toExternalData),
onDragStart: args => {
if (enableDragging.current) {
setDragging(true);