Skip to content

Commit

Permalink
Refactor lifecycle
Browse files Browse the repository at this point in the history
  • Loading branch information
clauderic committed Jun 21, 2024
1 parent a4d9150 commit e0d80f5
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 142 deletions.
7 changes: 7 additions & 0 deletions .changeset/refactor-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@dnd-kit/abstract': patch
'@dnd-kit/dom': patch
'@dnd-kit/react': patch
---

Refactor the lifecycle to allow `manager` to be optional and provided later during the lifecycle of `draggable` / `droppable` / `sortable` instances.
11 changes: 6 additions & 5 deletions packages/abstract/src/core/entities/draggable/draggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface Input<T extends Data = Data> extends EntityInput<T> {
export class Draggable<T extends Data = Data> extends Entity<T> {
constructor(
{modifiers, type, sensors, ...input}: Input<T>,
public manager: DragDropManager
manager: DragDropManager | undefined
) {
super(input, manager);

Expand All @@ -31,12 +31,15 @@ export class Draggable<T extends Data = Data> extends Entity<T> {
#modifiers: Modifier[] | undefined;

public set modifiers(modifiers: Modifiers | undefined) {
const {manager} = this;

this.#modifiers?.forEach((modifier) => modifier.destroy());

if (!manager) return;
this.#modifiers = modifiers?.map((modifier) => {
const {plugin, options} = descriptor(modifier);

return new plugin(this.manager, options);
return new plugin(manager, options);
});
}

Expand All @@ -52,9 +55,7 @@ export class Draggable<T extends Data = Data> extends Entity<T> {
*/
@derived
public get isDragSource() {
const {dragOperation} = this.manager;

return dragOperation.source?.id === this.id;
return this.manager?.dragOperation.source?.id === this.id;
}

public destroy() {
Expand Down
4 changes: 2 additions & 2 deletions packages/abstract/src/core/entities/droppable/droppable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class Droppable<T extends Data = Data> extends Entity<T> {
type,
...input
}: Input<T>,
public manager: DragDropManager
manager: DragDropManager | undefined
) {
super(input, manager);

Expand Down Expand Up @@ -91,7 +91,7 @@ export class Droppable<T extends Data = Data> extends Entity<T> {

@derived
public get isDropTarget() {
return this.manager.dragOperation.target?.id === this.id;
return this.manager?.dragOperation.target?.id === this.id;
}

public refreshShape() {
Expand Down
16 changes: 8 additions & 8 deletions packages/abstract/src/core/entities/entity/entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {effects, reactive, type Effect} from '@dnd-kit/state';
import {reactive, type Effect} from '@dnd-kit/state';

import type {DragDropManager} from '../../manager/index.ts';
import {DragDropManager} from '../../manager/index.ts';
import type {Data, UniqueIdentifier} from './types.ts';

interface Options {
Expand Down Expand Up @@ -36,7 +36,7 @@ export class Entity<T extends Data = Data> {
* @param input - An object containing the initial properties of the entity.
* @param manager - The manager that controls the drag and drop operations.
*/
constructor(input: Input<T>, manager: DragDropManager) {
constructor(input: Input<T>, manager: DragDropManager | undefined) {
const {
effects: getEffects = getDefaultEffects,
id,
Expand All @@ -54,23 +54,23 @@ export class Entity<T extends Data = Data> {
this.effects = () => [
() => {
// Re-run this effect whenever the `id` changes
const {id: _} = this;
const {id: _, manager} = this;

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

manager.registry.register(this);
manager?.registry.register(this);

return () => manager.registry.unregister(this);
return () => manager?.registry.unregister(this);
},
...getEffects(),
];
this.destroy = this.destroy.bind(this);

if (options?.register !== false) {
queueMicrotask(() => {
manager.registry.register(this);
this.manager?.registry.register(this);
});
}
}
Expand Down Expand Up @@ -109,6 +109,6 @@ export class Entity<T extends Data = Data> {
* @returns void
*/
public destroy(): void {
this.manager.registry.unregister(this);
this.manager?.registry.unregister(this);
}
}
1 change: 1 addition & 0 deletions packages/abstract/src/core/manager/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class DragDropManager<
this.plugins = [CollisionNotifier, ...plugins];
this.modifiers = modifiers;
this.sensors = sensors;
this.destroy = this.destroy.bind(this);
}

get plugins(): Plugin<any>[] {
Expand Down
5 changes: 4 additions & 1 deletion packages/dom/src/core/entities/draggable/draggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,17 @@ export class Draggable<T extends Data = Data> extends AbstractDraggable<T> {
feedback = 'default',
...input
}: Input<T>,
public manager: AbstractDragDropManager<any, any>
manager: AbstractDragDropManager<any, any> | undefined
) {
super(
{
effects: () => [
...effects(),
() => {
const {manager} = this;

if (!manager) return;

const sensors = this.sensors?.map(descriptor) ?? [
...manager.sensors,
];
Expand Down
13 changes: 8 additions & 5 deletions packages/dom/src/core/entities/droppable/droppable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface Input<T extends Data = Data>
export class Droppable<T extends Data = Data> extends AbstractDroppable<T> {
constructor(
{element, effects = () => [], ...input}: Input<T>,
public manager: AbstractDragDropManager<any, any>
manager: AbstractDragDropManager<any, any> | undefined
) {
const {collisionDetector = defaultCollisionDetection} = input;

Expand All @@ -38,6 +38,8 @@ export class Droppable<T extends Data = Data> extends AbstractDroppable<T> {
...effects(),
() => {
const {element, manager} = this;
if (!manager) return;

const {dragOperation} = manager;

if (element && dragOperation.status.initialized) {
Expand Down Expand Up @@ -92,6 +94,9 @@ export class Droppable<T extends Data = Data> extends AbstractDroppable<T> {
},
() => {
const {manager} = this;

if (!manager) return;

const {dragOperation} = manager;
const {status} = dragOperation;
const source = untracked(() => dragOperation.source);
Expand All @@ -105,9 +110,7 @@ export class Droppable<T extends Data = Data> extends AbstractDroppable<T> {
}
},
() => {
const {manager} = this;

if (manager.dragOperation.status.initialized) {
if (this.manager?.dragOperation.status.initialized) {
return () => {
this.shape = undefined;
};
Expand All @@ -127,7 +130,7 @@ export class Droppable<T extends Data = Data> extends AbstractDroppable<T> {
* If a droppable target mounts during a drag operation, assume it is visible
* so that we can update its shape immediately.
*/
if (manager.dragOperation.status.initialized) {
if (this.manager?.dragOperation.status.initialized) {
this.visible = true;
}
}
Expand Down
35 changes: 26 additions & 9 deletions packages/dom/src/sortable/sortable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,20 @@ export class Sortable<T extends Data = Data> {
plugins = defaultPlugins,
...input
}: SortableInput<T>,
public manager: DragDropManager<any, any>
manager: DragDropManager<any, any> | undefined
) {
this.droppable = new SortableDroppable<T>(input, manager, this);
this.draggable = new SortableDraggable<T>(
{
...input,
effects: () => [
() =>
this.manager.monitor.addEventListener('dragstart', () => {
this.manager?.monitor.addEventListener('dragstart', () => {
this.initialIndex = this.index;
this.previousIndex = this.index;
}),
() => {
const {index, previousIndex} = this;
const {index, previousIndex, manager: _} = this;

// Re-run this effect whenever the index changes
if (index === previousIndex) {
Expand All @@ -144,6 +144,13 @@ export class Sortable<T extends Data = Data> {
this.droppable.disabled = !target;
}
},
() => {
const {manager} = this;

for (const plugin of plugins) {
manager?.registry.register(plugin);
}
},
...inputEffects(),
],
type,
Expand All @@ -153,10 +160,7 @@ export class Sortable<T extends Data = Data> {
this
);

for (const plugin of plugins) {
manager.registry.register(plugin);
}

this.manager = manager;
this.index = index;
this.previousIndex = index;
this.initialIndex = index;
Expand All @@ -174,11 +178,15 @@ export class Sortable<T extends Data = Data> {
untracked(() => {
const {manager, transition} = this;
const {shape} = this.droppable;

if (!manager) return;

const {idle} = manager.dragOperation.status;

if (!shape || !transition || (idle && !transition.idle)) {
return;
}

scheduler.schedule(() => {
const {element} = this.droppable;

Expand Down Expand Up @@ -220,6 +228,15 @@ export class Sortable<T extends Data = Data> {
});
}

public get manager(): DragDropManager<any, any> | undefined {
return this.draggable.manager as any;
}

public set manager(manager: DragDropManager<any, any> | undefined) {
this.draggable.manager = manager as any;
this.droppable.manager = manager as any;
}

public set element(element: Element | undefined) {
this.draggable.element = element;
this.droppable.element = element;
Expand Down Expand Up @@ -336,7 +353,7 @@ export class Sortable<T extends Data = Data> {
export class SortableDraggable<T extends Data> extends Draggable<T> {
constructor(
input: DraggableInput<T>,
manager: DragDropManager,
manager: DragDropManager | undefined,
public sortable: Sortable<T>
) {
super(input, manager);
Expand All @@ -350,7 +367,7 @@ export class SortableDraggable<T extends Data> extends Draggable<T> {
export class SortableDroppable<T extends Data> extends Droppable<T> {
constructor(
input: DraggableInput<T>,
manager: DragDropManager,
manager: DragDropManager | undefined,
public sortable: Sortable<T>
) {
super(input, manager);
Expand Down
29 changes: 17 additions & 12 deletions packages/helpers/src/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,21 @@ function mutate<
if (sourceIndex === -1 || targetIndex === -1) {
return items;
}
const {dragOperation} = source.manager;

// Reconcile optimistic updates
if (
!dragOperation.canceled &&
'index' in source &&
typeof source.index === 'number'
) {
const projectedSourceIndex = source.index;

if (projectedSourceIndex !== sourceIndex) {
return mutation(items, sourceIndex, projectedSourceIndex);

if (source.manager) {
const {dragOperation} = source.manager;

// Reconcile optimistic updates
if (
!dragOperation.canceled &&
'index' in source &&
typeof source.index === 'number'
) {
const projectedSourceIndex = source.index;

if (projectedSourceIndex !== sourceIndex) {
return mutation(items, sourceIndex, projectedSourceIndex);
}
}
}

Expand Down Expand Up @@ -116,6 +119,8 @@ function mutate<
}
}

if (!source.manager) return items;

const {dragOperation} = source.manager;
const position = dragOperation.position.current;

Expand Down
Loading

0 comments on commit e0d80f5

Please sign in to comment.