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 2, 2025
1 parent d098369 commit 8ecd7dd
Show file tree
Hide file tree
Showing 14 changed files with 328 additions and 290 deletions.
109 changes: 68 additions & 41 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,13 @@ export const useDropTarget = <D extends DNDData = DNDData>(
return withClosestEdge;
},
onDrag: (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>);
}
updateDragOver(args, options.onDrag);
},
onDragEnter: (args: DropTargetDragEvent<D>) => {
updateDragOver(args, options.onDragEnter);
},
onDragLeave: (args: DropTargetDragEvent<D>) => {
options.onDragLeave?.(args);
},
onDropTargetChange: (args: DropTargetDropEvent<D>) => {
args = getAdaptedEventArgs(options, args);
Expand Down
57 changes: 57 additions & 0 deletions packages/frontend/component/src/ui/dnd/monitor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
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';

type MonitorGetFeedback = Parameters<
NonNullable<Parameters<typeof monitorForElements>[0]['canMonitor']>
>[0];

type MonitorGet<T> = T | ((data: MonitorGetFeedback) => T);

export interface MonitorOptions {
canMonitor?: MonitorGet<boolean>;
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 useMonitor = (
getOptions: () => MonitorOptions = () => ({}),
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;
}
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 @@ -23,6 +23,7 @@ export const menuTrigger = style({
height: 0,
pointerEvents: 'none',
});

export const indicator = style({
width: 29,
height: 15,
Expand All @@ -48,7 +49,7 @@ export const indicatorInner = style({
transition: 'all 0.5s cubic-bezier(0.16, 1, 0.3, 1)',

selectors: {
'[data-is-dragging="true"] &': {
'[data-is-dragging="true"] &, &:active': {
width: 24,
height: 2,
},
Expand Down
Loading

0 comments on commit 8ecd7dd

Please sign in to comment.