Files
AFFiNE-Mirror/libs/components/layout/src/workspace-sidebar/page-tree/DndTree.tsx
2022-08-12 13:56:27 +08:00

144 lines
4.5 KiB
TypeScript
Executable File

import React, { useMemo } from 'react';
import {
DndContext,
DragOverlay,
DropAnimation,
MeasuringStrategy,
PointerSensor,
defaultDropAnimation,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
SortableContext,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { DndTreeItem } from './tree-item';
import type { TreeItems } from './types';
import { usePageTree } from './use-page-tree';
import { getChildCount } from './utils';
const measuring = {
droppable: {
strategy: MeasuringStrategy.Always,
},
};
const dropAnimation: DropAnimation = {
...defaultDropAnimation,
// dragSourceOpacity: 0.5,
};
export type DndTreeProps = {
defaultItems?: TreeItems;
indentationWidth?: number;
collapsible?: boolean;
removable?: boolean;
};
/**
* Currently does not support drag and drop using the keyboard.
*/
export function DndTree(props: DndTreeProps) {
const { indentationWidth = 20, collapsible, removable } = props;
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 8 } })
);
const {
items,
activeId,
flattenedItems,
projected,
handleDragStart,
handleDragMove,
handleDragOver,
handleDragEnd,
handleDragCancel,
handleRemove,
handleCollapse,
} = usePageTree(props);
const sortedIds = useMemo(
() => flattenedItems.map(({ id }) => id),
[flattenedItems]
);
const activeItem = useMemo(
() =>
activeId ? flattenedItems.find(({ id }) => id === activeId) : null,
[activeId, flattenedItems]
);
return (
<DndContext
sensors={sensors}
measuring={measuring}
onDragStart={handleDragStart}
onDragMove={handleDragMove}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
<SortableContext
items={sortedIds}
strategy={verticalListSortingStrategy}
>
{/* <button onClick={() => handleAdd()}> add top node</button> */}
{flattenedItems.map(
({ id, title, children, collapsed, depth }) => {
return (
<DndTreeItem
key={id}
id={id}
// value={id}
value={title}
collapsed={Boolean(
collapsed && children.length
)}
depth={
id === activeId && projected
? projected.depth
: depth
}
indentationWidth={indentationWidth}
childCount={children.length}
onCollapse={
collapsible && children.length
? () => handleCollapse(id)
: undefined
}
onRemove={
removable
? () => handleRemove(id)
: undefined
}
/>
);
}
)}
<DragOverlay
dropAnimation={dropAnimation}
// TODO fix drag offset when the position of the page tree changes
style={{ marginTop: '-100px' }}
>
{activeId && activeItem ? (
<DndTreeItem
id={activeId}
// value={activeId}
value={activeItem.title}
depth={activeItem.depth}
clone={true}
childCount={getChildCount(items, activeId) + 1}
indentationWidth={indentationWidth}
/>
) : null}
</DragOverlay>
</SortableContext>
</DndContext>
);
}