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;