diff --git a/packages/abstract/src/core/collision/notifier.ts b/packages/abstract/src/core/collision/notifier.ts index 64cba42f..94d791b6 100644 --- a/packages/abstract/src/core/collision/notifier.ts +++ b/packages/abstract/src/core/collision/notifier.ts @@ -5,7 +5,7 @@ import {CorePlugin} from '../plugins/index.ts'; import {defaultPreventable} from '../manager/events.ts'; export class CollisionNotifier extends CorePlugin { - constructor(manager: DragDropManager) { + constructor(manager: DragDropManager) { super(manager); this.destroy = effect(() => { diff --git a/packages/abstract/src/core/entities/draggable/draggable.ts b/packages/abstract/src/core/entities/draggable/draggable.ts index b0a993b4..fd159ed4 100644 --- a/packages/abstract/src/core/entities/draggable/draggable.ts +++ b/packages/abstract/src/core/entities/draggable/draggable.ts @@ -16,10 +16,13 @@ export interface Input extends EntityInput { export type DraggableStatus = 'idle' | 'dragging' | 'dropping'; -export class Draggable extends Entity { +export class Draggable< + T extends Data = Data, + U extends DragDropManager = DragDropManager, +> extends Entity { constructor( {modifiers, type, sensors, ...input}: Input, - manager: DragDropManager | undefined + manager: U | undefined ) { super(input, manager); diff --git a/packages/abstract/src/core/entities/droppable/droppable.ts b/packages/abstract/src/core/entities/droppable/droppable.ts index 40aad4b2..027992ff 100644 --- a/packages/abstract/src/core/entities/droppable/droppable.ts +++ b/packages/abstract/src/core/entities/droppable/droppable.ts @@ -17,7 +17,10 @@ export interface Input extends EntityInput { type?: Type; } -export class Droppable extends Entity { +export class Droppable< + T extends Data = Data, + U extends DragDropManager = DragDropManager, +> extends Entity { constructor( { accept, @@ -26,7 +29,7 @@ export class Droppable extends Entity { type, ...input }: Input, - manager: DragDropManager | undefined + manager: U | undefined ) { super(input, manager); diff --git a/packages/abstract/src/core/entities/entity/entity.ts b/packages/abstract/src/core/entities/entity/entity.ts index 88b44254..9770f1ad 100644 --- a/packages/abstract/src/core/entities/entity/entity.ts +++ b/packages/abstract/src/core/entities/entity/entity.ts @@ -1,4 +1,4 @@ -import {reactive, type Effect} from '@dnd-kit/state'; +import {CleanupFunction, reactive, type Effect} from '@dnd-kit/state'; import {DragDropManager} from '../../manager/index.ts'; import type {Data, UniqueIdentifier} from './types.ts'; @@ -16,14 +16,17 @@ export interface Input { * * @template T - The type of data associated with the entity. */ -export class Entity { +export class Entity< + T extends Data = Data, + U extends DragDropManager = DragDropManager, +> { /** * Creates a new instance of the `Entity` class. * * @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, manager: DragDropManager | undefined) { + constructor(input: Input, manager: U | undefined) { const {effects, id, data = {}, disabled = false} = input; let previousId = id; @@ -60,7 +63,7 @@ export class Entity { * The manager that controls the drag and drop operations. */ @reactive - public accessor manager: DragDropManager | undefined; + public accessor manager: U | undefined; /** * The unique identifier of the entity. @@ -85,6 +88,22 @@ export class Entity { */ public effects: () => Effect[]; + /** + * A method that registers the entity with the manager. + * @returns CleanupFunction | void + */ + public register(): CleanupFunction | void { + return this.manager?.registry.register(this); + } + + /** + * A method that unregisters the entity from the manager. + * @returns void + */ + public unregister(): void { + this.manager?.registry.unregister(this); + } + /** * A method that cleans up the entity when it is no longer needed. * @returns void diff --git a/packages/abstract/src/core/manager/manager.ts b/packages/abstract/src/core/manager/manager.ts index e04859b9..a9d20cde 100644 --- a/packages/abstract/src/core/manager/manager.ts +++ b/packages/abstract/src/core/manager/manager.ts @@ -20,10 +20,7 @@ export type DragDropManagerInput> = { renderer?: Renderer; }; -export class DragDropManager< - T extends Draggable = Draggable, - U extends Droppable = Droppable, -> { +export class DragDropManager { public actions: DragActions>; public collisionObserver: CollisionObserver; public dragOperation: DragOperation; diff --git a/packages/abstract/src/core/manager/types.ts b/packages/abstract/src/core/manager/types.ts index 6d22850c..04c2b727 100644 --- a/packages/abstract/src/core/manager/types.ts +++ b/packages/abstract/src/core/manager/types.ts @@ -1,6 +1,7 @@ import type {DragDropManager} from './manager.ts'; -export type InferDraggable

= P extends DragDropManager ? T : never; +export type InferDraggable

= + P extends DragDropManager ? T : never; export type InferDroppable

= P extends DragDropManager ? T : never; diff --git a/packages/abstract/src/core/modifiers/modifier.ts b/packages/abstract/src/core/modifiers/modifier.ts index ece70d22..8e4b5116 100644 --- a/packages/abstract/src/core/modifiers/modifier.ts +++ b/packages/abstract/src/core/modifiers/modifier.ts @@ -11,7 +11,7 @@ import type {DragDropManager} from '../manager/index.ts'; export type ModifierOptions = PluginOptions; export class Modifier< - T extends DragDropManager = DragDropManager, + T extends DragDropManager = DragDropManager, U extends ModifierOptions = ModifierOptions, > extends Plugin { constructor( diff --git a/packages/abstract/src/core/sensors/sensor.ts b/packages/abstract/src/core/sensors/sensor.ts index 5979638c..eeb5ebc6 100644 --- a/packages/abstract/src/core/sensors/sensor.ts +++ b/packages/abstract/src/core/sensors/sensor.ts @@ -1,7 +1,7 @@ import {CleanupFunction} from '@dnd-kit/state'; import type {DragDropManager} from '../manager/index.ts'; -import type {Draggable} from '../entities/index.ts'; +import type {Draggable, Droppable} from '../entities/index.ts'; import { Plugin, type PluginConstructor, @@ -12,7 +12,7 @@ import { export type SensorOptions = PluginOptions; export abstract class Sensor< - T extends DragDropManager = DragDropManager, + T extends DragDropManager = DragDropManager, U extends SensorOptions = SensorOptions, > extends Plugin { constructor( diff --git a/packages/abstract/src/modifiers/axis.ts b/packages/abstract/src/modifiers/axis.ts index a172ceb8..85c4abc6 100644 --- a/packages/abstract/src/modifiers/axis.ts +++ b/packages/abstract/src/modifiers/axis.ts @@ -1,8 +1,10 @@ import { - DragOperation, configurator, Modifier, - DragDropManager, + type DragOperation, + type DragDropManager, + type Draggable, + type Droppable, } from '@dnd-kit/abstract'; interface Options { @@ -10,7 +12,10 @@ interface Options { value: number; } -export class AxisModifier extends Modifier { +export class AxisModifier extends Modifier< + DragDropManager, + Options +> { apply({transform}: DragOperation) { if (!this.options) { return transform; diff --git a/packages/abstract/src/modifiers/snap.ts b/packages/abstract/src/modifiers/snap.ts index 207be96e..fa534079 100644 --- a/packages/abstract/src/modifiers/snap.ts +++ b/packages/abstract/src/modifiers/snap.ts @@ -1,15 +1,20 @@ import { - DragOperation, configurator, Modifier, - DragDropManager, + type DragOperation, + type DragDropManager, + type Draggable, + type Droppable, } from '@dnd-kit/abstract'; interface Options { size: number | {x: number; y: number}; } -export class SnapModifier extends Modifier { +export class SnapModifier extends Modifier< + DragDropManager, + Options +> { apply({transform}: DragOperation) { const {size = 20} = this.options ?? {}; const x = typeof size === 'number' ? size : size.x; diff --git a/packages/dom/src/core/entities/draggable/draggable.ts b/packages/dom/src/core/entities/draggable/draggable.ts index 556fcf64..cdab0be0 100644 --- a/packages/dom/src/core/entities/draggable/draggable.ts +++ b/packages/dom/src/core/entities/draggable/draggable.ts @@ -3,12 +3,7 @@ import { Sensor, descriptor, } from '@dnd-kit/abstract'; -import type { - Data, - DraggableInput, - DragDropManager as AbstractDragDropManager, - Modifiers, -} from '@dnd-kit/abstract'; +import type {Data, DraggableInput, Modifiers} from '@dnd-kit/abstract'; import {reactive} from '@dnd-kit/state'; import type {DragDropManager} from '../../manager/index.ts'; @@ -33,7 +28,7 @@ export class Draggable extends AbstractDraggable { feedback = 'default', ...input }: Input, - manager: AbstractDragDropManager | undefined + manager: DragDropManager | undefined ) { super( { diff --git a/packages/dom/src/core/entities/droppable/droppable.ts b/packages/dom/src/core/entities/droppable/droppable.ts index 42470495..5e332f04 100644 --- a/packages/dom/src/core/entities/droppable/droppable.ts +++ b/packages/dom/src/core/entities/droppable/droppable.ts @@ -2,7 +2,6 @@ import {Droppable as AbstractDroppable} from '@dnd-kit/abstract'; import type { Data, DroppableInput as AbstractDroppableInput, - DragDropManager as AbstractDragDropManager, } from '@dnd-kit/abstract'; import {defaultCollisionDetection} from '@dnd-kit/collision'; import type {CollisionDetector} from '@dnd-kit/collision'; @@ -15,6 +14,8 @@ import { getFirstScrollableAncestor, } from '@dnd-kit/dom/utilities'; +import type {DragDropManager} from '../../manager/manager.ts'; + type OptionalInput = 'collisionDetector'; export interface Input @@ -26,7 +27,7 @@ export interface Input export class Droppable extends AbstractDroppable { constructor( {element, effects = () => [], ...input}: Input, - manager: AbstractDragDropManager | undefined + manager: DragDropManager | undefined ) { const {collisionDetector = defaultCollisionDetection} = input; diff --git a/packages/dom/src/sortable/sortable.ts b/packages/dom/src/sortable/sortable.ts index 2f15e1c6..a1beb595 100644 --- a/packages/dom/src/sortable/sortable.ts +++ b/packages/dom/src/sortable/sortable.ts @@ -1,5 +1,5 @@ import {batch, reactive, untracked} from '@dnd-kit/state'; -import {CollisionPriority} from '@dnd-kit/abstract'; +import {CollisionPriority, Entity} from '@dnd-kit/abstract'; import type { Data, PluginConstructor, @@ -351,7 +351,22 @@ export class Sortable { return this.droppable.accepts(draggable); } - public destroy() {} + public register() { + this.manager?.registry.register(this.draggable); + this.manager?.registry.register(this.droppable); + + return () => this.unregister(); + } + + public unregister() { + this.manager?.registry.unregister(this.draggable); + this.manager?.registry.unregister(this.droppable); + } + + public destroy() { + this.draggable.destroy(); + this.droppable.destroy(); + } } export class SortableDraggable extends Draggable { diff --git a/packages/react/src/core/context/context.ts b/packages/react/src/core/context/context.ts index 1ec7ac49..651c9531 100644 --- a/packages/react/src/core/context/context.ts +++ b/packages/react/src/core/context/context.ts @@ -3,6 +3,8 @@ import {createContext} from 'react'; import {DragDropManager} from '@dnd-kit/dom'; +export const defaultManager = new DragDropManager(); + export const DragDropContext = createContext( - new DragDropManager() + defaultManager ); diff --git a/packages/react/src/core/context/index.ts b/packages/react/src/core/context/index.ts deleted file mode 100644 index d2911e3a..00000000 --- a/packages/react/src/core/context/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {DragDropProvider} from './DragDropProvider.tsx'; diff --git a/packages/react/src/core/draggable/index.ts b/packages/react/src/core/draggable/index.ts deleted file mode 100644 index d931d7c3..00000000 --- a/packages/react/src/core/draggable/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {useDraggable} from './useDraggable.ts'; -export type {UseDraggableInput} from './useDraggable.ts'; diff --git a/packages/react/src/core/draggable/useDraggable.ts b/packages/react/src/core/draggable/useDraggable.ts index c7445959..0e56abe4 100644 --- a/packages/react/src/core/draggable/useDraggable.ts +++ b/packages/react/src/core/draggable/useDraggable.ts @@ -21,14 +21,14 @@ export function useDraggable( const handle = currentValue(input.handle); const element = currentValue(input.element); const draggable = useInstance( - () => + (manager) => new Draggable( { ...input, handle, element, }, - undefined + manager ) ); const isDragSource = useComputed(() => draggable.isDragSource); diff --git a/packages/react/src/core/droppable/index.ts b/packages/react/src/core/droppable/index.ts deleted file mode 100644 index 79f95526..00000000 --- a/packages/react/src/core/droppable/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {useDroppable} from './useDroppable.ts'; -export type {UseDroppableInput} from './useDroppable.ts'; diff --git a/packages/react/src/core/droppable/useDroppable.ts b/packages/react/src/core/droppable/useDroppable.ts index 453c62cd..f923bd62 100644 --- a/packages/react/src/core/droppable/useDroppable.ts +++ b/packages/react/src/core/droppable/useDroppable.ts @@ -19,13 +19,13 @@ export function useDroppable( const {collisionDetector, data, disabled, id, accept, type} = input; const element = currentValue(input.element); const droppable = useInstance( - () => + (manager) => new Droppable( { ...input, element, }, - undefined + manager ) ); const isDropTarget = useComputed(() => droppable.isDropTarget); diff --git a/packages/react/src/core/hooks/useInstance.ts b/packages/react/src/core/hooks/useInstance.ts index a00361fa..c96ac9ff 100644 --- a/packages/react/src/core/hooks/useInstance.ts +++ b/packages/react/src/core/hooks/useInstance.ts @@ -1,17 +1,30 @@ import {useEffect, useState} from 'react'; -import type {DragDropManager, Entity} from '@dnd-kit/abstract'; +import type {DragDropManager} from '@dnd-kit/abstract'; +import type {CleanupFunction} from '@dnd-kit/state'; import {useDragDropManager} from './useDragDropManager.ts'; +import {defaultManager} from '../context/context.ts'; -export function useInstance(initializer: () => T): T { +export interface Instance< + T extends DragDropManager = DragDropManager, +> { + manager: T | undefined; + register(): CleanupFunction | void; +} + +export function useInstance( + initializer: (manager: DragDropManager | undefined) => T +): T { const manager = useDragDropManager() ?? undefined; - const [instance] = useState(() => initializer()); + const [instance] = useState(() => + initializer(manager === defaultManager ? undefined : manager) + ); useEffect(() => { - instance.manager = manager as DragDropManager | undefined; + instance.manager = manager; // Register returns an unregister callback - return manager?.registry.register(instance); + return instance.register(); }, [instance, manager]); return instance; diff --git a/packages/react/src/core/index.ts b/packages/react/src/core/index.ts index a62292dd..4f56f197 100644 --- a/packages/react/src/core/index.ts +++ b/packages/react/src/core/index.ts @@ -1,11 +1,19 @@ 'use client'; -export {DragDropProvider} from './context/index.ts'; +export {DragDropProvider} from './context/DragDropProvider.tsx'; -export {useDraggable} from './draggable/index.ts'; +export { + useDraggable, + type UseDraggableInput, +} from './draggable/useDraggable.ts'; -export {useDroppable} from './droppable/index.ts'; +export { + useDroppable, + type UseDroppableInput, +} from './droppable/useDroppable.ts'; export {useDragDropManager} from './hooks/useDragDropManager.ts'; export {useDragOperation} from './hooks/useDragOperation.ts'; + +export {useInstance} from './hooks/useInstance.ts'; diff --git a/packages/react/src/sortable/useSortable.ts b/packages/react/src/sortable/useSortable.ts index 85fc3138..1dc4a088 100644 --- a/packages/react/src/sortable/useSortable.ts +++ b/packages/react/src/sortable/useSortable.ts @@ -3,13 +3,12 @@ import {deepEqual} from '@dnd-kit/state'; import {type Data} from '@dnd-kit/abstract'; import {Sortable, defaultSortableTransition} from '@dnd-kit/dom/sortable'; import type {SortableInput} from '@dnd-kit/dom/sortable'; -import {useDragDropManager} from '@dnd-kit/react'; +import {useDragDropManager, useInstance} from '@dnd-kit/react'; import { useComputed, - useConstant, - useOnValueChange, useImmediateEffect as immediateEffect, useIsomorphicLayoutEffect as layoutEffect, + useOnValueChange, } from '@dnd-kit/react/hooks'; import {currentValue, type RefOrValue} from '@dnd-kit/react/utilities'; @@ -39,7 +38,7 @@ export function useSortable(input: UseSortableInput) { const handle = currentValue(input.handle); const element = currentValue(input.element); const target = currentValue(input.target); - const sortable = useConstant(() => { + const sortable = useInstance((manager) => { return new Sortable( { ...input, @@ -48,24 +47,10 @@ export function useSortable(input: UseSortableInput) { target, feedback, }, - undefined + manager ); }); - useEffect(() => { - sortable.manager = manager ?? undefined; - - if (!manager) return; - - manager.registry.register(sortable.draggable); - manager.registry.register(sortable.droppable); - - return () => { - manager.registry.unregister(sortable.draggable); - manager.registry.unregister(sortable.droppable); - }; - }, [sortable, manager]); - const isDropTarget = useComputed(() => sortable.isDropTarget); const isDragSource = useComputed(() => sortable.isDragSource); const status = useComputed(() => sortable.status);