Skip to content

Commit

Permalink
refactor(core): remove dndkit in split view
Browse files Browse the repository at this point in the history
  • Loading branch information
pengx17 committed Jan 5, 2025
1 parent be0de6d commit bada613
Show file tree
Hide file tree
Showing 20 changed files with 546 additions and 422 deletions.
120 changes: 80 additions & 40 deletions packages/frontend/component/src/ui/dnd/drop-target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';

import { shallowEqual } from '../../utils';
import { DNDContext } from './context';
import type { DNDData, fromExternalData } from './types';

Expand Down Expand Up @@ -152,6 +153,15 @@ function dropTargetGet<T, D extends DNDData>(
}) as any;
}

const shallowUpdater =
<T>(newState: T) =>
(state: T | null): T => {
if (state && shallowEqual(state, newState)) {
return state;
}
return newState;
};

export interface DropTargetOptions<D extends DNDData = DNDData> {
data?: DropTargetGet<D['dropTarget'], D>;
canDrop?: DropTargetGet<boolean, D>;
Expand All @@ -168,6 +178,8 @@ export interface DropTargetOptions<D extends DNDData = DNDData> {
};
onDrop?: (data: DropTargetDropEvent<D>) => void;
onDrag?: (data: DropTargetDragEvent<D>) => void;
onDragEnter?: (data: DropTargetDragEvent<D>) => void;
onDragLeave?: (data: DropTargetDragEvent<D>) => void;
/**
* external data adapter.
* Will use the external data adapter from the context if not provided.
Expand Down Expand Up @@ -232,6 +244,55 @@ export const useDropTarget = <D extends DNDData = DNDData>(
const dropTargetOptions = useMemo(() => {
const wrappedCanDrop = dropTargetGet(options.canDrop, options);
let _element: HTMLElement | null = null;

const updateDragOver = (
args: DropTargetDragEvent<D>,
handler?: (data: DropTargetDragEvent<D>) => void
) => {
args = getAdaptedEventArgs(options, args);
if (
args.location.current.dropTargets[0]?.element === dropTargetRef.current
) {
if (enableDraggedOverDraggable.current) {
setDraggedOverDraggable(shallowUpdater(args.source));
}
let instruction = null;
let closestEdge = null;
if (options.treeInstruction) {
instruction = extractInstruction(args.self.data);
setTreeInstruction(shallowUpdater(instruction));
if (dropTargetRef.current) {
dropTargetRef.current.dataset['treeInstruction'] =
instruction?.type;
}
}
if (options.closestEdge) {
closestEdge = extractClosestEdge(args.self.data);
setClosestEdge(shallowUpdater(closestEdge));
}
if (enableDropEffect.current) {
setDropEffect(shallowUpdater(args.self.dropEffect));
}
if (enableDraggedOverPosition.current) {
const rect = args.self.element.getBoundingClientRect();
const { clientX, clientY } = args.location.current.input;
setDraggedOverPosition(
shallowUpdater({
relativeX: clientX - rect.x,
relativeY: clientY - rect.y,
clientX: clientX,
clientY: clientY,
})
);
}
handler?.({
...args,
treeInstruction: instruction,
closestEdge,
} as DropTargetDropEvent<D>);
}
};

return {
get element() {
if (!_element) {
Expand Down Expand Up @@ -332,47 +393,26 @@ export const useDropTarget = <D extends DNDData = DNDData>(
return withClosestEdge;
},
onDrag: (args: DropTargetDragEvent<D>) => {
updateDragOver(args, options.onDrag);
},
onDragEnter: (args: DropTargetDragEvent<D>) => {
updateDragOver(args, options.onDragEnter);
},
onDragLeave: (args: DropTargetDragEvent<D>) => {
args = getAdaptedEventArgs(options, args);
if (
args.location.current.dropTargets[0]?.element ===
dropTargetRef.current
) {
if (enableDraggedOverDraggable.current) {
setDraggedOverDraggable(args.source);
}
let instruction = null;
let closestEdge = null;
if (options.treeInstruction) {
instruction = extractInstruction(args.self.data);
setTreeInstruction(instruction);
if (dropTargetRef.current) {
dropTargetRef.current.dataset['treeInstruction'] =
instruction?.type;
}
}
if (options.closestEdge) {
closestEdge = extractClosestEdge(args.self.data);
setClosestEdge(closestEdge);
}
if (enableDropEffect.current) {
setDropEffect(args.self.dropEffect);
}
if (enableDraggedOverPosition.current) {
const rect = args.self.element.getBoundingClientRect();
const { clientX, clientY } = args.location.current.input;
setDraggedOverPosition({
relativeX: clientX - rect.x,
relativeY: clientY - rect.y,
clientX: clientX,
clientY: clientY,
});
}
options.onDrag?.({
...args,
treeInstruction: instruction,
closestEdge,
} as DropTargetDropEvent<D>);
}

const withClosestEdge = options.closestEdge
? attachClosestEdge(args.self.data, {
element: args.self.element,
input: args.location.current.input,
allowedEdges: options.closestEdge.allowedEdges,
})
: args.self.data;

options.onDragLeave?.({
...args,
self: { ...args.self, data: withClosestEdge },
});
},
onDropTargetChange: (args: DropTargetDropEvent<D>) => {
args = getAdaptedEventArgs(options, args);
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/component/src/ui/dnd/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './context';
export * from './draggable';
export * from './drop-indicator';
export * from './drop-target';
export * from './monitor';
export * from './types';
65 changes: 65 additions & 0 deletions packages/frontend/component/src/ui/dnd/monitor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import type {
BaseEventPayload,
ElementDragType,
} from '@atlaskit/pragmatic-drag-and-drop/types';
import { useEffect, useMemo } from 'react';

import type { DNDData } from './types';

type MonitorGetFeedback<D extends DNDData = DNDData> = Parameters<
NonNullable<Parameters<typeof monitorForElements>[0]['canMonitor']>
>[0] & {
source: {
data: D['draggable'];
};
};

type MonitorGet<T, D extends DNDData = DNDData> =
| T
| ((data: MonitorGetFeedback<D>) => T);

export interface MonitorOptions<D extends DNDData = DNDData> {
canMonitor?: MonitorGet<boolean, D>;
onDragStart?: (data: BaseEventPayload<ElementDragType>) => void;
onDrag?: (data: BaseEventPayload<ElementDragType>) => void;
onDrop?: (data: BaseEventPayload<ElementDragType>) => void;
onDropTargetChange?: (data: BaseEventPayload<ElementDragType>) => void;
}

function monitorGet<T>(
get: T
): T extends undefined
? undefined
: T extends MonitorGet<infer I>
? (args: MonitorGetFeedback) => I
: never {
if (get === undefined) {
return undefined as any;
}
return ((args: MonitorGetFeedback) =>
typeof get === 'function' ? (get as any)(args) : get) as any;
}

export const useDndMonitor = <D extends DNDData = DNDData>(
getOptions: () => MonitorOptions<D> = () => ({}),
deps: any[] = []
) => {
const options = useMemo(() => {
const opts = getOptions();
return {
...opts,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...deps, getOptions]);

useEffect(() => {
return monitorForElements({
canMonitor: monitorGet(options.canMonitor),
onDragStart: monitorGet(options.onDragStart),
onDrag: monitorGet(options.onDrag),
onDrop: monitorGet(options.onDrop),
onDropTargetChange: monitorGet(options.onDropTargetChange),
});
}, [options]);
};
2 changes: 2 additions & 0 deletions packages/frontend/component/src/ui/dnd/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import type { dropTargetForExternal } from '@atlaskit/pragmatic-drag-and-drop/external/adapter';

export type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';

export interface DNDData<
Draggable extends Record<string, unknown> = Record<string, unknown>,
DropTarget extends Record<string, unknown> = Record<string, unknown>,
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/component/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './observe-intersection';
export * from './observe-resize';
export * from './shallow-equal';
export { startScopedViewTransition } from './view-transition';
export * from './with-unit';
34 changes: 34 additions & 0 deletions packages/frontend/component/src/utils/shallow-equal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// credit: https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/shallowEqual.js
export function shallowEqual(objA: any, objB: any) {
if (Object.is(objA, objB)) {
return true;
}

if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}

const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

if (keysA.length !== keysB.length) {
return false;
}

// Test for A's keys different from B.
for (const key of keysA) {
if (
!Object.prototype.hasOwnProperty.call(objB, key) ||
!Object.is(objA[key], objB[key])
) {
return false;
}
}

return true;
}
3 changes: 0 additions & 3 deletions packages/frontend/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
"@blocksuite/icons": "2.1.75",
"@capacitor/app": "^6.0.2",
"@capacitor/browser": "^6.0.4",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dotlottie/player-component": "^2.7.12",
"@emotion/cache": "^11.14.0",
"@emotion/react": "^11.14.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { shallowEqual } from '@affine/component';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import type { Tag } from '@affine/env/filter';
import { useI18n } from '@affine/i18n';
Expand Down Expand Up @@ -34,7 +35,6 @@ import type {
TagListItemProps,
TagMeta,
} from './types';
import { shallowEqual } from './utils';

export const ItemGroupHeader = memo(function ItemGroupHeader<
T extends ListItem,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { shallowEqual } from '@affine/component';
import { DEFAULT_SORT_KEY } from '@affine/env/constant';
import { atom } from 'jotai';
import { selectAtom } from 'jotai/utils';
Expand All @@ -10,7 +11,6 @@ import type {
MetaRecord,
VirtualizedListProps,
} from './types';
import { shallowEqual } from './utils';

// for ease of use in the component tree
// note: must use selectAtom to access this atom for efficiency
Expand Down
35 changes: 0 additions & 35 deletions packages/frontend/core/src/components/page-list/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,38 +56,3 @@ export const betweenDaysAgo = (
): boolean => {
return !withinDaysAgo(date, days0) && withinDaysAgo(date, days1);
};

// credit: https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/shallowEqual.js
export function shallowEqual(objA: any, objB: any) {
if (Object.is(objA, objB)) {
return true;
}

if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}

const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

if (keysA.length !== keysB.length) {
return false;
}

// Test for A's keys different from B.
for (const key of keysA) {
if (
!Object.prototype.hasOwnProperty.call(objB, key) ||
!Object.is(objA[key], objB[key])
) {
return false;
}
}

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ export class Workbench extends Entity {
return views.at(activeIndex) || views[0];
});

draggingView$ = new LiveData<{
view: View;
index: number;
} | null>(null);
draggingOver$ = new LiveData<{
view: View;
index: number;
} | null>(null);

location$ = LiveData.computed(get => {
return get(get(this.activeView$).location$);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createContext } from 'react';

import type { View } from '../../entities/view';

export interface SplitViewContextType {
draggingView: View | null;
setDraggingView: (view: View | null) => void;
droppingIndex: number | null;
setDroppingIndex: (index: number | null) => void;
}

export const SplitViewContext = createContext<SplitViewContextType>({
draggingView: null,
setDraggingView: () => {},
droppingIndex: null,
setDroppingIndex: () => {},
});
Loading

0 comments on commit bada613

Please sign in to comment.