Skip to content

Commit

Permalink
Various bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
clauderic committed Jun 4, 2024
1 parent 5d57464 commit d6d6620
Show file tree
Hide file tree
Showing 21 changed files with 358 additions and 282 deletions.
File renamed without changes.
5 changes: 5 additions & 0 deletions .changeset/restore-focus-keyboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@dnd-kit/dom': patch
---

- Only restore focus after drop if the `activatorEvent` is a keyboard event.
2 changes: 1 addition & 1 deletion apps/stories/stories/react/Sortable/Grid/Grid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const VariableSizes: Story = {
...defaultArgs,
itemCount: 14,
collisionDetector: pointerIntersection,
getItemStyle(_, index) {
getItemStyle(_: number, index: number) {
if (index === 0 || index === 10) {
return {
width: 320,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useRef, useState} from 'react';
import React, {useEffect, useRef, useState} from 'react';
import type {PropsWithChildren} from 'react';
import {flushSync} from 'react-dom';
import {CollisionPriority} from '@dnd-kit/abstract';
Expand Down
55 changes: 24 additions & 31 deletions apps/stories/stories/vanilla/Droppable/VanillaDroppableExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,37 @@ export const DroppableExample = createVanillaStory(() => {
{
id: 'draggable',
element: draggableElement,
effects(draggable) {
return [
() => {
const {status} = manager.dragOperation;

if (
draggable.isDragSource &&
(status.dragging || status.dropping)
) {
draggableElement.setAttribute('data-shadow', 'true');

return () => {
draggableElement.removeAttribute('data-shadow');
};
}
},
];
},
effects: () => [
() => {
const {status} = manager.dragOperation;

if (draggable.isDragSource && (status.dragging || status.dropped)) {
draggableElement.setAttribute('data-shadow', 'true');

return () => {
draggableElement.removeAttribute('data-shadow');
};
}
},
],
},
manager
);
const droppable = new Droppable(
{
id: 'droppable',
element: droppableElement,
effects(droppable) {
return [
() => {
if (droppable.isDropTarget) {
droppableElement.setAttribute('data-highlight', 'true');

return () => {
droppableElement.removeAttribute('data-highlight');
};
}
},
];
},
effects: () => [
() => {
if (droppable.isDropTarget) {
droppableElement.setAttribute('data-highlight', 'true');

return () => {
droppableElement.removeAttribute('data-highlight');
};
}
},
],
},
manager
);
Expand Down
31 changes: 26 additions & 5 deletions packages/abstract/src/core/entities/entity/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ export interface Input<T extends Data = Data, U extends Entity<T> = Entity<T>> {
id: UniqueIdentifier;
data?: T | null;
disabled?: boolean;
effects?: <Instance extends U>(instance: Instance) => Effect[];
effects?(): Effect[];
}

function getDefaultEffects(): Effect[] {
return [];
}

/**
Expand All @@ -27,25 +31,42 @@ export class Entity<T extends Data = Data> {
input: Input<T>,
public manager: DragDropManager
) {
const {effects: getInputEffects, id, data = null, disabled = false} = input;
const {
effects: getEffects = getDefaultEffects,
id,
data = null,
disabled = false,
} = input;

let previousId = id;

this.id = id;
this.data = data;
this.disabled = disabled;

queueMicrotask(() => {
const inputEffects = getInputEffects?.(this) ?? [];
manager.registry.register(this);

this.destroy = effects(
const cleanupEffects = effects(
() => {
// Re-run this effect whenever the `id` changes
const {id: _} = this;

if (id === previousId) {
return;
}

manager.registry.register(this);

return () => manager.registry.unregister(this);
},
...inputEffects
...getEffects()
);

this.destroy = () => {
manager.registry.unregister(this);
cleanupEffects();
};
});
}

Expand Down
42 changes: 27 additions & 15 deletions packages/abstract/src/core/manager/dragOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export enum Status {
Idle = 'idle',
Initializing = 'initializing',
Dragging = 'dragging',
Dropping = 'dropping',
Dropped = 'dropped',
}

export type Serializable = {
Expand All @@ -35,7 +35,8 @@ export interface DragOperation<
initialized: boolean;
initializing: boolean;
dragging: boolean;
dropping: boolean;
dragended: boolean;
dropped: boolean;
idle: boolean;
};
get shape(): {
Expand Down Expand Up @@ -77,7 +78,8 @@ export function DragOperationManager<
const initialized = computed(() => status.value !== Status.Idle);
const initializing = computed(() => status.value === Status.Initializing);
const idle = computed(() => status.value === Status.Idle);
const dropping = computed(() => status.value === Status.Dropping);
const dropped = computed(() => status.value === Status.Dropped);
const dragended = signal<boolean>(true);
let previousSource: T | undefined;
const source = computed<T | null>(() => {
const identifier = sourceIdentifier.value;
Expand Down Expand Up @@ -116,7 +118,8 @@ export function DragOperationManager<
initializing: initializing.peek(),
initialized: initialized.peek(),
dragging: dragging.peek(),
dropping: dropping.peek(),
dragended: dragended.peek(),
dropped: dropped.peek(),
},
shape:
initialShape && currentShape
Expand Down Expand Up @@ -161,8 +164,11 @@ export function DragOperationManager<
get dragging() {
return dragging.value;
},
get dropping() {
return dropping.value;
get dragended() {
return dragended.value;
},
get dropped() {
return dropped.value;
},
},
get shape(): DragOperation['shape'] {
Expand Down Expand Up @@ -218,17 +224,20 @@ export function DragOperationManager<

targetIdentifier.value = id;

monitor.dispatch(
'dragover',
defaultPreventable({
operation: snapshot(operation),
})
);
if (status.peek() === Status.Dragging) {
monitor.dispatch(
'dragover',
defaultPreventable({
operation: snapshot(operation),
})
);
}

return manager.renderer.rendering;
},
start({event, coordinates}: {event: Event; coordinates: Coordinates}) {
batch(() => {
dragended.value = false;
canceled.value = false;
activatorEvent.value = event;
position.reset(coordinates);
Expand Down Expand Up @@ -309,14 +318,17 @@ export function DragOperationManager<
return output;
};
const end = () => {
/* Wait for the renderer to finish rendering before finalizing the drag operation */
manager.renderer.rendering.then(() => {
status.value = Status.Dropping;

status.value = Status.Dropped;
manager.renderer.rendering.then(reset);
});
};

canceled.value = eventCanceled;
batch(() => {
dragended.value = true;
canceled.value = eventCanceled;
});

monitor.dispatch('dragend', {
operation: snapshot(operation),
Expand Down
64 changes: 37 additions & 27 deletions packages/dom/src/core/entities/draggable/draggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,49 @@ export class Draggable<T extends Data = Data> extends AbstractDraggable<T> {
public sensors: Sensors | undefined;

constructor(
{element, handle, feedback = 'default', sensors, ...input}: Input<T>,
{
element,
effects = () => [],
handle,
feedback = 'default',
sensors,
...input
}: Input<T>,
public manager: AbstractDragDropManager<any, any>
) {
super(input, manager);
super(
{
effects: () => [
...effects(),
() => {
const sensors = this.sensors?.map(descriptor) ?? [
...manager.sensors,
];
const unbindFunctions = sensors.map((entry) => {
const sensorInstance =
entry instanceof Sensor
? entry
: manager.registry.register(entry.plugin);
const options =
entry instanceof Sensor ? undefined : entry.options;

const unbind = sensorInstance.bind(this, options);
return unbind;
});

return function cleanup() {
unbindFunctions.forEach((unbind) => unbind());
};
},
],
...input,
},
manager
);

this.element = element;
this.handle = handle;
this.feedback = feedback;
this.sensors = sensors;

const cleanupEffect = effect(() => {
const sensors = this.sensors?.map(descriptor) ?? [...manager.sensors];
const unbindFunctions = sensors.map((entry) => {
const sensorInstance =
entry instanceof Sensor
? entry
: manager.registry.register(entry.plugin);
const options = entry instanceof Sensor ? undefined : entry.options;

const unbind = sensorInstance.bind(this, options);
return unbind;
});

return function cleanup() {
unbindFunctions.forEach((unbind) => unbind());
};
});

const {destroy} = this;

this.destroy = () => {
cleanupEffect();
destroy();
};
}
}
Loading

0 comments on commit d6d6620

Please sign in to comment.