From 7e9f26a4c7e8ed86e10a1ec10f8cbde2ad3916df Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 29 Dec 2025 09:18:31 +0100 Subject: [PATCH] fix(cdk/drag-drop): make item and list easier to tree shake Currently we have the `DragDrop` service that creates the `DragRef` and `DropListRef` which contain most of the implementation for the directives. The problem is that by going through the service, we can't tree shake the list if the app is only using the item. These changes move the creation into separate functions. --- goldens/cdk/drag-drop/index.api.md | 11 +++++- src/cdk/drag-drop/directives/drag.ts | 6 +-- src/cdk/drag-drop/directives/drop-list.ts | 8 ++-- src/cdk/drag-drop/drag-drop.ts | 48 +++++++---------------- src/cdk/drag-drop/drag-ref.ts | 32 +++++++++++++++ src/cdk/drag-drop/drop-list-ref.ts | 20 +++++++++- src/cdk/drag-drop/public-api.ts | 11 +++++- 7 files changed, 90 insertions(+), 46 deletions(-) diff --git a/goldens/cdk/drag-drop/index.api.md b/goldens/cdk/drag-drop/index.api.md index 63b89f3124e8..ed37827ef231 100644 --- a/goldens/cdk/drag-drop/index.api.md +++ b/goldens/cdk/drag-drop/index.api.md @@ -9,6 +9,7 @@ import { ElementRef } from '@angular/core'; import { EventEmitter } from '@angular/core'; import * as i0 from '@angular/core'; import { InjectionToken } from '@angular/core'; +import { Injector } from '@angular/core'; import { NgZone } from '@angular/core'; import { Observable } from 'rxjs'; import { OnChanges } from '@angular/core'; @@ -298,16 +299,24 @@ export class CdkDropListGroup implements OnDestroy { // @public export function copyArrayItem(currentArray: T[], targetArray: T[], currentIndex: number, targetIndex: number): void; +// @public +export function createDragRef(injector: Injector, element: ElementRef | HTMLElement, config?: DragRefConfig): DragRef; + +// @public +export function createDropListRef(injector: Injector, element: ElementRef | HTMLElement): DropListRef; + // @public export type DragAxis = 'x' | 'y'; // @public export type DragConstrainPosition = (userPointerPosition: Point, dragRef: DragRef, dimensions: DOMRect, pickupPositionInElement: Point) => Point; -// @public +// @public @deprecated export class DragDrop { constructor(...args: unknown[]); + // @deprecated createDrag(element: ElementRef | HTMLElement, config?: DragRefConfig): DragRef; + // @deprecated createDropList(element: ElementRef | HTMLElement): DropListRef; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; diff --git a/src/cdk/drag-drop/directives/drag.ts b/src/cdk/drag-drop/directives/drag.ts index 679b4ef2baba..6b538025dfdf 100644 --- a/src/cdk/drag-drop/directives/drag.ts +++ b/src/cdk/drag-drop/directives/drag.ts @@ -43,9 +43,8 @@ import {CDK_DRAG_HANDLE, CdkDragHandle} from './drag-handle'; import {CdkDragPlaceholder} from './drag-placeholder'; import {CdkDragPreview} from './drag-preview'; import {CDK_DRAG_PARENT} from '../drag-parent'; -import {DragRef, Point, PreviewContainer, DragConstrainPosition} from '../drag-ref'; +import {DragRef, Point, PreviewContainer, DragConstrainPosition, createDragRef} from '../drag-ref'; import type {CdkDropList} from './drop-list'; -import {DragDrop} from '../drag-drop'; import {CDK_DRAG_CONFIG, DragDropConfig, DragStartDelay, DragAxis} from './config'; import {assertElementNode} from './assertions'; import {DragDropRegistry} from '../drag-drop-registry'; @@ -222,9 +221,8 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { constructor() { const dropContainer = this.dropContainer; const config = inject(CDK_DRAG_CONFIG, {optional: true}); - const dragDrop = inject(DragDrop); - this._dragRef = dragDrop.createDrag(this.element, { + this._dragRef = createDragRef(this._injector, this.element, { dragStartThreshold: config && config.dragStartThreshold != null ? config.dragStartThreshold : 5, pointerDirectionChangeThreshold: diff --git a/src/cdk/drag-drop/directives/drop-list.ts b/src/cdk/drag-drop/directives/drop-list.ts index a1fc256c8a1a..8ff7eb3654fb 100644 --- a/src/cdk/drag-drop/directives/drop-list.ts +++ b/src/cdk/drag-drop/directives/drop-list.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, booleanAttribute, inject, + Injector, } from '@angular/core'; import {Directionality} from '../../bidi'; import {_IdGenerator} from '../../a11y'; @@ -24,9 +25,8 @@ import {ScrollDispatcher} from '../../scrolling'; import {CDK_DROP_LIST, CdkDrag} from './drag'; import {CdkDragDrop, CdkDragEnter, CdkDragExit, CdkDragSortEvent} from '../drag-events'; import {CDK_DROP_LIST_GROUP, CdkDropListGroup} from './drop-list-group'; -import {DropListRef} from '../drop-list-ref'; +import {createDropListRef, DropListRef} from '../drop-list-ref'; import {DragRef} from '../drag-ref'; -import {DragDrop} from '../drag-drop'; import {DropListOrientation, DragAxis, DragDropConfig, CDK_DRAG_CONFIG} from './config'; import {merge, Subject} from 'rxjs'; import {startWith, takeUntil} from 'rxjs/operators'; @@ -197,14 +197,14 @@ export class CdkDropList implements OnDestroy { constructor(...args: unknown[]); constructor() { - const dragDrop = inject(DragDrop); const config = inject(CDK_DRAG_CONFIG, {optional: true}); + const injector = inject(Injector); if (typeof ngDevMode === 'undefined' || ngDevMode) { assertElementNode(this.element.nativeElement, 'cdkDropList'); } - this._dropListRef = dragDrop.createDropList(this.element); + this._dropListRef = createDropListRef(injector, this.element); this._dropListRef.data = this; if (config) { diff --git a/src/cdk/drag-drop/drag-drop.ts b/src/cdk/drag-drop/drag-drop.ts index 4098f036521e..5b253e26069f 100644 --- a/src/cdk/drag-drop/drag-drop.ts +++ b/src/cdk/drag-drop/drag-drop.ts @@ -6,29 +6,19 @@ * found in the LICENSE file at https://angular.dev/license */ -import {Injectable, NgZone, ElementRef, inject, RendererFactory2, DOCUMENT} from '@angular/core'; - -import {ViewportRuler} from '../scrolling'; -import {DragRef, DragRefConfig} from './drag-ref'; -import {DropListRef} from './drop-list-ref'; -import {DragDropRegistry} from './drag-drop-registry'; - -/** Default configuration to be used when creating a `DragRef`. */ -const DEFAULT_CONFIG = { - dragStartThreshold: 5, - pointerDirectionChangeThreshold: 5, -}; +import {Injectable, ElementRef, inject, Injector} from '@angular/core'; +import {createDragRef, DragRef, DragRefConfig} from './drag-ref'; +import {createDropListRef, DropListRef} from './drop-list-ref'; /** * Service that allows for drag-and-drop functionality to be attached to DOM elements. + * @deprecated Use the `createDragRef` or `createDropListRef` function for better tree shaking. + * Will be removed in v23. + * @breaking-change 23.0.0 */ @Injectable({providedIn: 'root'}) export class DragDrop { - private _document = inject(DOCUMENT); - private _ngZone = inject(NgZone); - private _viewportRuler = inject(ViewportRuler); - private _dragDropRegistry = inject(DragDropRegistry); - private _renderer = inject(RendererFactory2).createRenderer(null, null); + private _injector = inject(Injector); constructor(...args: unknown[]); constructor() {} @@ -37,33 +27,23 @@ export class DragDrop { * Turns an element into a draggable item. * @param element Element to which to attach the dragging functionality. * @param config Object used to configure the dragging behavior. + * @deprecated Use the `createDragRef` function that provides better tree shaking. + * @breaking-change 23.0.0 */ createDrag( element: ElementRef | HTMLElement, - config: DragRefConfig = DEFAULT_CONFIG, + config?: DragRefConfig, ): DragRef { - return new DragRef( - element, - config, - this._document, - this._ngZone, - this._viewportRuler, - this._dragDropRegistry, - this._renderer, - ); + return createDragRef(this._injector, element, config); } /** * Turns an element into a drop list. * @param element Element to which to attach the drop list functionality. + * @deprecated Use the `createDropListRef` function that provides better tree shaking. + * @breaking-change 23.0.0 */ createDropList(element: ElementRef | HTMLElement): DropListRef { - return new DropListRef( - element, - this._dragDropRegistry, - this._document, - this._ngZone, - this._viewportRuler, - ); + return createDropListRef(this._injector, element); } } diff --git a/src/cdk/drag-drop/drag-ref.ts b/src/cdk/drag-drop/drag-ref.ts index 83a0292d86ef..ecdba8a490b7 100644 --- a/src/cdk/drag-drop/drag-ref.ts +++ b/src/cdk/drag-drop/drag-ref.ts @@ -12,10 +12,13 @@ import {coerceElement} from '../coercion'; import {_getEventTarget, _getShadowRoot} from '../platform'; import {ViewportRuler} from '../scrolling'; import { + DOCUMENT, ElementRef, EmbeddedViewRef, + Injector, NgZone, Renderer2, + RendererFactory2, TemplateRef, ViewContainerRef, signal, @@ -124,6 +127,35 @@ const dragImportantProperties = new Set([ */ export type PreviewContainer = 'global' | 'parent' | ElementRef | HTMLElement; +/** + * Creates a `DragRef` for an element, turning it into a draggable item. + * @param injector Injector used to resolve dependencies. + * @param element Element to which to attach the dragging functionality. + * @param config Object used to configure the dragging behavior. + */ +export function createDragRef( + injector: Injector, + element: ElementRef | HTMLElement, + config: DragRefConfig = { + dragStartThreshold: 5, + pointerDirectionChangeThreshold: 5, + }, +): DragRef { + const renderer = + injector.get(Renderer2, null, {optional: true}) || + injector.get(RendererFactory2).createRenderer(null, null); + + return new DragRef( + element, + config, + injector.get(DOCUMENT), + injector.get(NgZone), + injector.get(ViewportRuler), + injector.get(DragDropRegistry), + renderer, + ); +} + /** * Reference to a draggable item. Used to manipulate or dispose of the item. */ diff --git a/src/cdk/drag-drop/drop-list-ref.ts b/src/cdk/drag-drop/drop-list-ref.ts index 2c2638981b9a..000b3c188691 100644 --- a/src/cdk/drag-drop/drop-list-ref.ts +++ b/src/cdk/drag-drop/drop-list-ref.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ElementRef, NgZone} from '@angular/core'; +import {DOCUMENT, ElementRef, Injector, NgZone} from '@angular/core'; import {Direction} from '../bidi'; import {coerceElement} from '../coercion'; import {ViewportRuler} from '../scrolling'; @@ -49,6 +49,24 @@ enum AutoScrollHorizontalDirection { RIGHT, } +/** + * Creates a `DropListRef` for an element, turning it into a drop list. + * @param injector Injector used to resolve dependencies. + * @param element Element to which to attach the drop list functionality. + */ +export function createDropListRef( + injector: Injector, + element: ElementRef | HTMLElement, +): DropListRef { + return new DropListRef( + element, + injector.get(DragDropRegistry), + injector.get(DOCUMENT), + injector.get(NgZone), + injector.get(ViewportRuler), + ); +} + /** * Reference to a drop list. Used to manipulate or dispose of the container. */ diff --git a/src/cdk/drag-drop/public-api.ts b/src/cdk/drag-drop/public-api.ts index 34d541d83211..fac63b2c0d7b 100644 --- a/src/cdk/drag-drop/public-api.ts +++ b/src/cdk/drag-drop/public-api.ts @@ -7,8 +7,15 @@ */ export {DragDrop} from './drag-drop'; -export {DragRef, DragRefConfig, Point, PreviewContainer, DragConstrainPosition} from './drag-ref'; -export {DropListRef} from './drop-list-ref'; +export { + DragRef, + DragRefConfig, + Point, + PreviewContainer, + DragConstrainPosition, + createDragRef, +} from './drag-ref'; +export {DropListRef, createDropListRef} from './drop-list-ref'; export {CDK_DRAG_PARENT} from './drag-parent'; export * from './drag-events';