Skip to content

Commit

Permalink
More progress
Browse files Browse the repository at this point in the history
- Introduce modifiers
- Update feedback plugins
- Fix drop animations
  • Loading branch information
clauderic committed Aug 2, 2023
1 parent 560a5a3 commit 77b83ab
Show file tree
Hide file tree
Showing 26 changed files with 553 additions and 281 deletions.
149 changes: 114 additions & 35 deletions apps/docs/stories/Droppable/DroppableExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,141 @@ import {closestCenter, CollisionDetector} from '@dnd-kit/collision';
import {Button, Dropzone} from '../components';
import {DraggableIcon} from '../icons';

const items = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20,
];

export function DroppableExample() {
const [parent, setParent] = useState<UniqueIdentifier | null>(null);
const draggableMarkup = <Draggable id="test-1" type="A" />;
const [items, setItems] = useState({
A: [
{id: 'A1', children: [{id: 'A1', type: 'A'}]},
{id: 'A2', children: []},
{id: 'A3', children: []},
{id: 'A4', children: []},
{id: 'A5', children: []},
{id: 'A6', children: []},
{id: 'A7', children: []},
],
B: [
{id: 'B1', children: []},
{id: 'B2', children: [{id: 'A2', type: 'A'}]},
{id: 'B3', children: []},
{id: 'B4', children: []},
],
C: [
{id: 'C1', children: []},
{id: 'C2', children: []},
{id: 'C3', children: []},
{id: 'C4', children: []},
],
});

return (
<DndContext
onDragEnd={(event) => {
console.log(event);
setParent(event.operation.target?.id ?? null);
onDragOver={(event) => {
const {source, target} = event.operation;

if (source && target) {
const [targetParentId] = String(target.id);
const [currentParentId] = String(source.data!.parent);

if (source.data!.parent !== target.id) {
setItems((items) => {
if (targetParentId !== currentParentId) {
return {
...items,
[currentParentId]: items[currentParentId].map((item) => {
if (item.id === source.data!.parent) {
return {
...item,
children: item.children.filter(
(child) => child.id !== source.id
),
};
}

return item;
}),
[targetParentId]: items[targetParentId].map((item) => {
if (item.id === target.id) {
return {
...item,
children: [
...item.children,
{id: source.id, type: source.type},
],
};
}

return item;
}),
};
} else {
return {
...items,
[targetParentId]: items[targetParentId].map((item) => {
if (item.id === target.id) {
return {
...item,
children: [
...item.children,
{id: source.id, type: source.type},
],
};
}

if (item.id === source.data!.parent) {
return {
...item,
children: item.children.filter(
(child) => child.id !== source.id
),
};
}

return item;
}),
};
}
});
}
}
}}
>
<div style={{display: 'flex', flexDirection: 'row'}}>
<div>
{items.map((id) => (
<Droppable
key={id}
id={`A${id}`}
accept={['A']}
collisionDetector={closestCenter}
>
{parent === `A${id}` ? draggableMarkup : null}
</Droppable>
))}
</div>

<div style={{maxHeight: '60vh', overflow: 'auto'}}>
{parent == null ? draggableMarkup : null}
<div style={{height: 20}} />
{/* <Draggable id="test-2" type="B" /> */}
{items.map((id) => (
<Droppable key={id} id={`B${id}`} accept={['A', 'B']}>
{parent === `B${id}` ? draggableMarkup : null}
</Droppable>
))}
</div>
<div style={{display: 'flex', flexDirection: 'row', gap: 20}}>
{Object.entries(items).map(([id, items]) => (
<div
key={id}
style={{display: 'flex', flexDirection: 'column', gap: 20}}
>
{items.map((item) => (
<Droppable
key={item.id}
id={item.id}
accept={['A']}
collisionDetector={closestCenter}
>
{item.children.map((child) => (
<Draggable id={child.id} type={child.type} parent={item.id} />
))}
</Droppable>
))}
</div>
))}
</div>
</DndContext>
);
}

interface DraggableProps {
id: UniqueIdentifier;
parent: UniqueIdentifier;
type?: UniqueIdentifier;
}

function Draggable({id, type}: DraggableProps) {
function Draggable({id, parent, type}: DraggableProps) {
const [element, setElement] = useState<Element | null>(null);
const [backgroundColor, setBackgroundColor] = useState('');
const activatorRef = useRef<HTMLButtonElement | null>(null);

const {isDragging} = useDraggable({
id,
data: {parent},
element,
activator: activatorRef,
type,
Expand Down
6 changes: 4 additions & 2 deletions apps/docs/stories/components/Dropzone/Dropzone.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.Dropzone {
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: flex-start;
position: relative;
padding-top: 80px;
text-align: center;
Expand Down
7 changes: 5 additions & 2 deletions packages/abstract/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ export type {
export {CollisionPriority} from './collision';
export type {Collision, CollisionDetector} from './collision';

export {Plugin} from './plugins';
export type {PluginConstructor} from './plugins';
export {Modifier} from './modifiers';
export type {ModifierConstructor} from './modifiers';

export {Draggable, Droppable} from './nodes';
export type {Data, Node, DraggableInput, DroppableInput} from './nodes';

export {Plugin} from './plugins';
export type {PluginConstructor} from './plugins';

export {Sensor} from './sensors';
export type {
SensorConstructor,
Expand Down
66 changes: 48 additions & 18 deletions packages/abstract/src/manager/dragOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import {batch, computed, signal} from '@dnd-kit/state';

import type {Draggable, Droppable} from '../nodes';

import type {DragDropRegistry} from './registry';
import type {DragDropMonitor} from './manager';
import type {DragDropManager} from './manager';

export enum Status {
Idle = 'idle',
Expand All @@ -15,19 +14,6 @@ export enum Status {
Dropping = 'dropped',
}

export interface Input<
T extends Draggable = Draggable,
U extends Droppable = Droppable,
> {
registry: DragDropRegistry<T, U>;
monitor: DragDropMonitor;
}

export type DragOperationManager<
T extends Draggable = Draggable,
U extends Droppable = Droppable,
> = ReturnType<typeof DragOperationManager<T, U>>;

export type Serializable = {
[key: string]: string | number | null | Serializable | Serializable[];
};
Expand All @@ -38,17 +24,30 @@ export interface DragOperation<
> {
status: Status;
position: Position;
transform: Coordinates;
initialized: boolean;
shape: Shape | null;
source: T | null;
target: U | null;
data?: Serializable;
}

export type DragActions<
T extends Draggable,
U extends Droppable,
V extends DragDropManager<T, U>,
> = ReturnType<typeof DragOperationManager<T, U, V>>['actions'];

export function DragOperationManager<
T extends Draggable = Draggable,
U extends Droppable = Droppable,
>({registry: {draggable, droppable}, monitor}: Input<T, U>) {
T extends Draggable,
U extends Droppable,
V extends DragDropManager<T, U>,
>(manager: V) {
const {
registry: {draggable, droppable},
monitor,
modifiers,
} = manager;
const status = signal<Status>(Status.Idle);
const shape = signal<Shape | null>(null);
const position = new Position({x: 0, y: 0});
Expand All @@ -64,6 +63,25 @@ export function DragOperationManager<
});
const dragging = computed(() => status.value === Status.Dragging);

const transform = computed(() => {
const {x, y} = position.delta;
let transform = {x, y};
const operation = {
source: source.peek() ?? null,
target: target.peek() ?? null,
initialized: status.peek() !== Status.Idle,
status: status.peek(),
shape: shape.peek(),
position,
};

for (const modifier of modifiers) {
transform = modifier.apply({...operation, transform});
}

return transform;
});

const operation: DragOperation<T, U> = {
get source() {
return source.value ?? null;
Expand All @@ -81,8 +99,15 @@ export function DragOperationManager<
return shape.value;
},
set shape(value: Shape | null) {
if (value && shape.peek()?.equals(value)) {
return;
}

shape.value = value;
},
get transform() {
return transform.value;
},
position,
};

Expand All @@ -98,6 +123,10 @@ export function DragOperationManager<
}

targetIdentifier.value = identifier;

monitor.dispatch('dragover', {
operation: snapshot(operation),
});
},
start(coordinates: Coordinates) {
status.value = Status.Initializing;
Expand Down Expand Up @@ -138,6 +167,7 @@ export function DragOperationManager<
status.value = Status.Idle;
sourceIdentifier.value = null;
targetIdentifier.value = null;
shape.value = null;
position.reset({x: 0, y: 0});
});
});
Expand Down
7 changes: 2 additions & 5 deletions packages/abstract/src/manager/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
export {DragDropManager} from './manager';
export type {
DragDropManagerInput,
DragDropConfiguration,
DragDropEvents,
} from './manager';
export type {DragDropManagerInput, DragDropConfiguration} from './manager';
export type {DragDropEvents} from './monitor';
export {Status as DragOperationStatus} from './dragOperation';
export type {DragOperation, DragOperationManager} from './dragOperation';
export type {DragDropRegistry} from './registry';
Expand Down
Loading

0 comments on commit 77b83ab

Please sign in to comment.