From 087bbb5a7301cf9bbe55bc947f77da806afa8bed Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sun, 2 Dec 2018 17:02:31 +0100 Subject: [PATCH] refactor(drag-drop): expose more private apis as protected Exposes more of the private drag&drop APIs as protected ones, in order to make it easier for people to implement their custom directives based on the CDK ones. Also moves out some of the private helper methods into functions. Fixes #14113. --- src/cdk/drag-drop/drag.ts | 64 +++++++++++------------ src/cdk/drag-drop/drop-list.ts | 48 ++++++++--------- src/cdk/drag-drop/public-api.ts | 1 + tools/public_api_guard/cdk/drag-drop.d.ts | 19 +++++++ 4 files changed, 76 insertions(+), 56 deletions(-) diff --git a/src/cdk/drag-drop/drag.ts b/src/cdk/drag-drop/drag.ts index 0e3b7d951354..333f5654270d 100644 --- a/src/cdk/drag-drop/drag.ts +++ b/src/cdk/drag-drop/drag.ts @@ -332,7 +332,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { rootElement.addEventListener('mousedown', this._pointerDown, activeEventListenerOptions); rootElement.addEventListener('touchstart', this._pointerDown, passiveEventListenerOptions); this._handles.changes.pipe(startWith(null)).subscribe(() => - toggleNativeDragInteractions(rootElement, this.getChildHandles().length > 0)); + toggleNativeDragInteractions(rootElement, this._getChildHandles().length > 0)); }); } @@ -349,7 +349,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { if (this._isDragging()) { // Since we move out the element to the end of the body while it's being // dragged, we have to make sure that it's removed if it gets destroyed. - this._removeElement(this._rootElement); + removeElement(this._rootElement); } } @@ -368,13 +368,13 @@ export class CdkDrag implements AfterViewInit, OnDestroy { } /** Gets only handles that are not inside descendant `CdkDrag` instances. */ - private getChildHandles() { + protected _getChildHandles() { return this._handles.filter(handle => handle._parentDrag === this); } /** Handler for the `mousedown`/`touchstart` events. */ _pointerDown = (event: MouseEvent | TouchEvent) => { - const handles = this.getChildHandles(); + const handles = this._getChildHandles(); // Delegate the event based on whether it started from a handle or the element itself. if (handles.length) { @@ -398,16 +398,16 @@ export class CdkDrag implements AfterViewInit, OnDestroy { * @param referenceElement Element that started the drag sequence. * @param event Browser event object that started the sequence. */ - private _initializeDragSequence(referenceElement: HTMLElement, event: MouseEvent | TouchEvent) { + protected _initializeDragSequence(referenceElement: HTMLElement, event: MouseEvent | TouchEvent) { // Always stop propagation for the event that initializes // the dragging sequence, in order to prevent it from potentially // starting another sequence for a draggable parent somewhere up the DOM tree. event.stopPropagation(); const isDragging = this._isDragging(); - const isTouchEvent = this._isTouchEvent(event); - const isAuxiliaryMouseButton = !isTouchEvent && (event as MouseEvent).button !== 0; - const isSyntheticEvent = !isTouchEvent && this._lastTouchEventTime && + const wasTouchEvent = isTouchEvent(event); + const isAuxiliaryMouseButton = !wasTouchEvent && (event as MouseEvent).button !== 0; + const isSyntheticEvent = !wasTouchEvent && this._lastTouchEventTime && this._lastTouchEventTime + MOUSE_EVENT_IGNORE_TIME > Date.now(); // If the event started from an element with the native HTML drag&drop, it'll interfere @@ -453,11 +453,11 @@ export class CdkDrag implements AfterViewInit, OnDestroy { } /** Starts the dragging sequence. */ - private _startDragSequence(event: MouseEvent | TouchEvent) { + protected _startDragSequence(event: MouseEvent | TouchEvent) { // Emit the event on the item before the one on the container. this.started.emit({source: this}); - if (this._isTouchEvent(event)) { + if (isTouchEvent(event)) { this._lastTouchEventTime = Date.now(); } @@ -482,7 +482,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { } /** Handler that is invoked when the user moves their pointer after they've initiated a drag. */ - private _pointerMove = (event: MouseEvent | TouchEvent) => { + protected _pointerMove = (event: MouseEvent | TouchEvent) => { if (!this._hasStartedDragging) { const pointerPosition = this._getPointerPositionOnPage(event); const distanceX = Math.abs(pointerPosition.x - this._pickupPositionOnPage.x); @@ -551,7 +551,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { } /** Handler that is invoked when the user lifts their pointer up, after initiating a drag. */ - private _pointerUp = (event: MouseEvent | TouchEvent) => { + protected _pointerUp = (event: MouseEvent | TouchEvent) => { if (!this._isDragging()) { return; } @@ -721,7 +721,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { const elementRect = this._rootElement.getBoundingClientRect(); const handleElement = referenceElement === this._rootElement ? null : referenceElement; const referenceRect = handleElement ? handleElement.getBoundingClientRect() : elementRect; - const point = this._isTouchEvent(event) ? event.targetTouches[0] : event; + const point = isTouchEvent(event) ? event.targetTouches[0] : event; const x = point.pageX - referenceRect.left - this._scrollPosition.left; const y = point.pageY - referenceRect.top - this._scrollPosition.top; @@ -778,20 +778,10 @@ export class CdkDrag implements AfterViewInit, OnDestroy { }); } - /** - * Helper to remove an element from the DOM and to do all the necessary null checks. - * @param element Element to be removed. - */ - private _removeElement(element: HTMLElement | null) { - if (element && element.parentNode) { - element.parentNode.removeChild(element); - } - } - /** Determines the point of the page that was touched by the user. */ private _getPointerPositionOnPage(event: MouseEvent | TouchEvent): Point { // `touches` will be empty for start/end events so we have to fall back to `changedTouches`. - const point = this._isTouchEvent(event) ? (event.touches[0] || event.changedTouches[0]) : event; + const point = isTouchEvent(event) ? (event.touches[0] || event.changedTouches[0]) : event; return { x: point.pageX - this._scrollPosition.left, @@ -826,15 +816,10 @@ export class CdkDrag implements AfterViewInit, OnDestroy { return point; } - /** Determines whether an event is a touch event. */ - private _isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent { - return event.type.startsWith('touch'); - } - /** Destroys the preview element and its ViewRef. */ private _destroyPreview() { if (this._preview) { - this._removeElement(this._preview); + removeElement(this._preview); } if (this._previewRef) { @@ -847,7 +832,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { /** Destroys the placeholder element and its ViewRef. */ private _destroyPlaceholder() { if (this._placeholder) { - this._removeElement(this._placeholder); + removeElement(this._placeholder); } if (this._placeholderRef) { @@ -885,7 +870,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { } /** Gets the root draggable element, based on the `rootElementSelector`. */ - private _getRootElement(): HTMLElement { + protected _getRootElement(): HTMLElement { const element = this.element.nativeElement; const rootElement = this.rootElementSelector ? getClosestMatchingAncestor(element, this.rootElementSelector) : null; @@ -950,3 +935,18 @@ function getClosestMatchingAncestor(element: HTMLElement, selector: string) { currentElement = currentElement.parentElement; } } + +/** + * Helper to remove an element from the DOM and to do all the necessary null checks. + * @param element Element to be removed. + */ +function removeElement(element: HTMLElement | null) { + if (element && element.parentNode) { + element.parentNode.removeChild(element); + } +} + +/** Determines whether an event is a touch event. */ +function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent { + return event.type.startsWith('touch'); +} diff --git a/src/cdk/drag-drop/drop-list.ts b/src/cdk/drag-drop/drop-list.ts index 5641ee6dbc69..fc89f25cd65f 100644 --- a/src/cdk/drag-drop/drop-list.ts +++ b/src/cdk/drag-drop/drop-list.ts @@ -196,14 +196,14 @@ export class CdkDropList implements OnInit, OnDestroy { _dragging = false; /** Cache of the dimensions of all the items and the sibling containers. */ - private _positionCache: PositionCache = {items: [], siblings: [], self: {} as ClientRect}; + protected _positionCache: PositionCache = {items: [], siblings: [], self: {} as ClientRect}; /** * Draggable items that are currently active inside the container. Includes the items * from `_draggables`, as well as any items that have been dragged in, but haven't * been dropped yet. */ - private _activeDraggables: CdkDrag[]; + protected _activeDraggables: CdkDrag[]; /** * Keeps track of the item that was last swapped with the dragged item, as @@ -382,10 +382,10 @@ export class CdkDropList implements OnInit, OnDestroy { // Round the transforms since some browsers will // blur the elements, for sub-pixel transforms. elementToOffset.style.transform = `translate3d(${Math.round(sibling.offset)}px, 0, 0)`; - this._adjustClientRect(sibling.clientRect, 0, offset); + adjustClientRect(sibling.clientRect, 0, offset); } else { elementToOffset.style.transform = `translate3d(0, ${Math.round(sibling.offset)}px, 0)`; - this._adjustClientRect(sibling.clientRect, offset, 0); + adjustClientRect(sibling.clientRect, offset, 0); } }); } @@ -487,7 +487,7 @@ export class CdkDropList implements OnInit, OnDestroy { } /** Resets the container to its initial state. */ - private _reset() { + protected _reset() { this._dragging = false; // TODO(crisbeto): may have to wait for the animations to finish. @@ -499,20 +499,6 @@ export class CdkDropList implements OnInit, OnDestroy { this._previousSwap.delta = 0; } - /** - * Updates the top/left positions of a `ClientRect`, as well as their bottom/right counterparts. - * @param clientRect `ClientRect` that should be updated. - * @param top Amount to add to the `top` position. - * @param left Amount to add to the `left` position. - */ - private _adjustClientRect(clientRect: ClientRect, top: number, left: number) { - clientRect.top += top; - clientRect.bottom = clientRect.top + clientRect.height; - - clientRect.left += left; - clientRect.right = clientRect.left + clientRect.width; - } - /** * Gets the index of an item in the drop container, based on the position of the user's pointer. * @param item Item that is being sorted. @@ -520,7 +506,7 @@ export class CdkDropList implements OnInit, OnDestroy { * @param pointerY Position of the user's pointer along the Y axis. * @param delta Direction in which the user is moving their pointer. */ - private _getItemIndexFromPointerPosition(item: CdkDrag, pointerX: number, pointerY: number, + protected _getItemIndexFromPointerPosition(item: CdkDrag, pointerX: number, pointerY: number, delta?: {x: number, y: number}) { const isHorizontal = this.orientation === 'horizontal'; @@ -555,7 +541,7 @@ export class CdkDropList implements OnInit, OnDestroy { * @param pointerX Coordinates along the X axis. * @param pointerY Coordinates along the Y axis. */ - private _isPointerNearDropContainer(pointerX: number, pointerY: number): boolean { + protected _isPointerNearDropContainer(pointerX: number, pointerY: number): boolean { const {top, right, bottom, left, width, height} = this._positionCache.self; const xThreshold = width * DROP_PROXIMITY_THRESHOLD; const yThreshold = height * DROP_PROXIMITY_THRESHOLD; @@ -570,7 +556,7 @@ export class CdkDropList implements OnInit, OnDestroy { * @param newPosition Position of the item where the current item should be moved. * @param delta Direction in which the user is moving. */ - private _getItemOffsetPx(currentPosition: ClientRect, newPosition: ClientRect, delta: 1 | -1) { + protected _getItemOffsetPx(currentPosition: ClientRect, newPosition: ClientRect, delta: 1 | -1) { const isHorizontal = this.orientation === 'horizontal'; let itemOffset = isHorizontal ? newPosition.left - currentPosition.left : newPosition.top - currentPosition.top; @@ -590,7 +576,7 @@ export class CdkDropList implements OnInit, OnDestroy { * @param siblings All of the items in the list. * @param delta Direction in which the user is moving. */ - private _getSiblingOffsetPx(currentIndex: number, + protected _getSiblingOffsetPx(currentIndex: number, siblings: ItemPositionCacheEntry[], delta: 1 | -1) { @@ -618,7 +604,7 @@ export class CdkDropList implements OnInit, OnDestroy { } /** Gets an array of unique drop lists that the current list is connected to. */ - private _getConnectedLists(): CdkDropList[] { + protected _getConnectedLists(): CdkDropList[] { const siblings = coerceArray(this.connectedTo).map(drop => { return typeof drop === 'string' ? this._dragDropRegistry.getDropContainer(drop)! : drop; }); @@ -665,3 +651,17 @@ function isInsideClientRect(clientRect: ClientRect, x: number, y: number) { const {top, bottom, left, right} = clientRect; return y >= top && y <= bottom && x >= left && x <= right; } + +/** + * Updates the top/left positions of a `ClientRect`, as well as their bottom/right counterparts. + * @param clientRect `ClientRect` that should be updated. + * @param top Amount to add to the `top` position. + * @param left Amount to add to the `left` position. + */ +function adjustClientRect(clientRect: ClientRect, top: number, left: number) { + clientRect.top += top; + clientRect.bottom = clientRect.top + clientRect.height; + + clientRect.left += left; + clientRect.right = clientRect.left + clientRect.width; +} diff --git a/src/cdk/drag-drop/public-api.ts b/src/cdk/drag-drop/public-api.ts index a4f5b1730024..20c44de7cdf1 100644 --- a/src/cdk/drag-drop/public-api.ts +++ b/src/cdk/drag-drop/public-api.ts @@ -17,3 +17,4 @@ export * from './drag-preview'; export * from './drag-placeholder'; export * from './drag-drop-module'; export * from './drag-drop-registry'; +export * from './drag-parent'; diff --git a/tools/public_api_guard/cdk/drag-drop.d.ts b/tools/public_api_guard/cdk/drag-drop.d.ts index 4d104fc6af89..755583814314 100644 --- a/tools/public_api_guard/cdk/drag-drop.d.ts +++ b/tools/public_api_guard/cdk/drag-drop.d.ts @@ -2,6 +2,8 @@ export declare const CDK_DRAG_CONFIG: InjectionToken; export declare function CDK_DRAG_CONFIG_FACTORY(): CdkDragConfig; +export declare const CDK_DRAG_PARENT: InjectionToken<{}>; + export declare const CDK_DROP_LIST_CONTAINER: InjectionToken>; export declare class CdkDrag implements AfterViewInit, OnDestroy { @@ -9,6 +11,8 @@ export declare class CdkDrag implements AfterViewInit, OnDestroy { _hasStartedDragging: boolean; _placeholderTemplate: CdkDragPlaceholder; _pointerDown: (event: TouchEvent | MouseEvent) => void; + protected _pointerMove: (event: TouchEvent | MouseEvent) => void; + protected _pointerUp: (event: TouchEvent | MouseEvent) => void; _previewTemplate: CdkDragPreview; boundaryElementSelector: string; data: T; @@ -26,7 +30,11 @@ export declare class CdkDrag implements AfterViewInit, OnDestroy { constructor( element: ElementRef, dropContainer: CdkDropListContainer, document: any, _ngZone: NgZone, _viewContainerRef: ViewContainerRef, _viewportRuler: ViewportRuler, _dragDropRegistry: DragDropRegistry, CdkDropListContainer>, _config: CdkDragConfig, _dir: Directionality); + protected _getChildHandles(): CdkDragHandle[]; + protected _getRootElement(): HTMLElement; + protected _initializeDragSequence(referenceElement: HTMLElement, event: MouseEvent | TouchEvent): void; _isDragging(): boolean; + protected _startDragSequence(event: MouseEvent | TouchEvent): void; getPlaceholderElement(): HTMLElement; getRootElement(): HTMLElement; ngAfterViewInit(): void; @@ -106,8 +114,10 @@ export interface CdkDragStart { } export declare class CdkDropList implements OnInit, OnDestroy { + protected _activeDraggables: CdkDrag[]; _draggables: QueryList; _dragging: boolean; + protected _positionCache: PositionCache; connectedTo: (CdkDropList | string)[] | CdkDropList | string; data: T; disabled: boolean; @@ -121,8 +131,17 @@ export declare class CdkDropList implements OnInit, OnDestroy { orientation: 'horizontal' | 'vertical'; sorted: EventEmitter>; constructor(element: ElementRef, _dragDropRegistry: DragDropRegistry>, _changeDetectorRef: ChangeDetectorRef, _dir?: Directionality | undefined, _group?: CdkDropListGroup> | undefined, _document?: any); + protected _getConnectedLists(): CdkDropList[]; + protected _getItemIndexFromPointerPosition(item: CdkDrag, pointerX: number, pointerY: number, delta?: { + x: number; + y: number; + }): number; + protected _getItemOffsetPx(currentPosition: ClientRect, newPosition: ClientRect, delta: 1 | -1): number; _getSiblingContainerFromPosition(item: CdkDrag, x: number, y: number): CdkDropList | null; + protected _getSiblingOffsetPx(currentIndex: number, siblings: ItemPositionCacheEntry[], delta: 1 | -1): number; _isOverContainer(x: number, y: number): boolean; + protected _isPointerNearDropContainer(pointerX: number, pointerY: number): boolean; + protected _reset(): void; _sortItem(item: CdkDrag, pointerX: number, pointerY: number, pointerDelta: { x: number; y: number;