Skip to content

Commit

Permalink
refactor(cdk/drag-drop): track directives in registry
Browse files Browse the repository at this point in the history
Switches to tracking the registered directive nodes in the registry instead of a private static field. This allows us to reuse the logic in the handles.
  • Loading branch information
crisbeto committed Dec 12, 2024
1 parent c04f84f commit 41b74bd
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 19 deletions.
27 changes: 8 additions & 19 deletions src/cdk/drag-drop/directives/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ import type {CdkDropList} from './drop-list';
import {DragDrop} from '../drag-drop';
import {CDK_DRAG_CONFIG, DragDropConfig, DragStartDelay, DragAxis} from './config';
import {assertElementNode} from './assertions';

const DRAG_HOST_CLASS = 'cdk-drag';
import {DragDropRegistry} from '../drag-drop-registry';

/**
* Injection token that can be used to reference instances of `CdkDropList`. It serves as
Expand All @@ -63,7 +62,7 @@ export const CDK_DROP_LIST = new InjectionToken<CdkDropList>('CdkDropList');
selector: '[cdkDrag]',
exportAs: 'cdkDrag',
host: {
'class': DRAG_HOST_CLASS,
'class': 'cdk-drag',
'[class.cdk-drag-disabled]': 'disabled',
'[class.cdk-drag-dragging]': '_dragRef.isDragging()',
},
Expand All @@ -78,9 +77,9 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
private _changeDetectorRef = inject(ChangeDetectorRef);
private _selfHandle = inject<CdkDragHandle>(CDK_DRAG_HANDLE, {optional: true, self: true});
private _parentDrag = inject<CdkDrag>(CDK_DRAG_PARENT, {optional: true, skipSelf: true});
private _dragDropRegistry = inject(DragDropRegistry);

private readonly _destroyed = new Subject<void>();
private static _dragInstances: CdkDrag[] = [];
private _handles = new BehaviorSubject<CdkDragHandle[]>([]);
private _previewTemplate: CdkDragPreview | null;
private _placeholderTemplate: CdkDragPlaceholder | null;
Expand Down Expand Up @@ -240,11 +239,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
zIndex: config?.zIndex,
});
this._dragRef.data = this;

// We have to keep track of the drag instances in order to be able to match an element to
// a drag instance. We can't go through the global registry of `DragRef`, because the root
// element could be different.
CdkDrag._dragInstances.push(this);
this._dragDropRegistry.registerDirectiveNode(this.element.nativeElement, this);

if (config) {
this._assignDefaults(config);
Expand Down Expand Up @@ -348,10 +343,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
this.dropContainer.removeItem(this);
}

const index = CdkDrag._dragInstances.indexOf(this);
if (index > -1) {
CdkDrag._dragInstances.splice(index, 1);
}
this._dragDropRegistry.removeDirectiveNode(this.element.nativeElement);

// Unnecessary in most cases, but used to avoid extra change detections with `zone-paths-rxjs`.
this._ngZone.runOutsideAngular(() => {
Expand Down Expand Up @@ -487,12 +479,9 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
// the item was projected into another item by something like `ngTemplateOutlet`.
let parent = this.element.nativeElement.parentElement;
while (parent) {
if (parent.classList.contains(DRAG_HOST_CLASS)) {
ref.withParent(
CdkDrag._dragInstances.find(drag => {
return drag.element.nativeElement === parent;
})?._dragRef || null,
);
const parentDrag = this._dragDropRegistry.getDragDirectiveForNode(parent);
if (parentDrag) {
ref.withParent(parentDrag._dragRef);
break;
}
parent = parent.parentElement;
Expand Down
35 changes: 35 additions & 0 deletions src/cdk/drag-drop/drag-drop-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {_CdkPrivateStyleLoader} from '@angular/cdk/private';
import {Observable, Observer, Subject, merge} from 'rxjs';
import type {DropListRef} from './drop-list-ref';
import type {DragRef} from './drag-ref';
import type {CdkDrag} from './directives/drag';

/** Event options that can be used to bind an active, capturing event. */
const activeCapturingEventOptions = normalizePassiveListenerOptions({
Expand Down Expand Up @@ -79,6 +80,13 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
*/
private _draggingPredicate = (item: DragRef) => item.isDragging();

/**
* Map tracking DOM nodes and their corresponding drag directives. Note that this is different
* from looking through the `_dragInstances` and getting their root node, because the root node
* isn't necessarily the node that the directive is set on.
*/
private _domNodesToDirectives: WeakMap<Node, CdkDrag> | null = null;

/**
* Emits the `touchmove` or `mousemove` events that are dispatched
* while the user is dragging a drag item instance.
Expand Down Expand Up @@ -262,9 +270,36 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
return merge(...streams);
}

/**
* Tracks the DOM node which has a draggable directive.
* @param node Node to track.
* @param dragRef Drag directive set on the node.
*/
registerDirectiveNode(node: Node, dragRef: CdkDrag): void {
this._domNodesToDirectives ??= new WeakMap();
this._domNodesToDirectives.set(node, dragRef);
}

/**
* Stops tracking a draggable directive node.
* @param node Node to stop tracking.
*/
removeDirectiveNode(node: Node): void {
this._domNodesToDirectives?.delete(node);
}

/**
* Gets the drag directive corresponding to a specific DOM node, if any.
* @param node Node for which to do the lookup.
*/
getDragDirectiveForNode(node: Node): CdkDrag | null {
return this._domNodesToDirectives?.get(node) || null;
}

ngOnDestroy() {
this._dragInstances.forEach(instance => this.removeDragItem(instance));
this._dropInstances.forEach(instance => this.removeDropContainer(instance));
this._domNodesToDirectives = null;
this._clearGlobalListeners();
this.pointerMove.complete();
this.pointerUp.complete();
Expand Down
3 changes: 3 additions & 0 deletions tools/public_api_guard/cdk/drag-drop.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,16 @@ export class DragDropModule {
// @public
export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
constructor(...args: unknown[]);
getDragDirectiveForNode(node: Node): CdkDrag | null;
isDragging(drag: DragRef): boolean;
// (undocumented)
ngOnDestroy(): void;
readonly pointerMove: Subject<TouchEvent | MouseEvent>;
readonly pointerUp: Subject<TouchEvent | MouseEvent>;
registerDirectiveNode(node: Node, dragRef: CdkDrag): void;
registerDragItem(drag: DragRef): void;
registerDropContainer(drop: DropListRef): void;
removeDirectiveNode(node: Node): void;
removeDragItem(drag: DragRef): void;
removeDropContainer(drop: DropListRef): void;
// @deprecated
Expand Down

0 comments on commit 41b74bd

Please sign in to comment.