mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(drag-drop): add the ability to customize how the position is constrained (#15137)
This commit is contained in:
committed by
Joey Perrott
parent
9d60a5da4c
commit
4704653cad
@@ -30,7 +30,7 @@ import {CdkDropList} from './drop-list';
|
||||
import {CdkDragHandle} from './drag-handle';
|
||||
import {CdkDropListGroup} from './drop-list-group';
|
||||
import {extendStyles} from '../drag-styling';
|
||||
import {DragRefConfig} from '../drag-ref';
|
||||
import {DragRefConfig, Point} from '../drag-ref';
|
||||
|
||||
const ITEM_HEIGHT = 25;
|
||||
const ITEM_WIDTH = 75;
|
||||
@@ -697,6 +697,24 @@ describe('CdkDrag', () => {
|
||||
expect(dragElement.style.transform).toBe('translate3d(100px, 100px, 0px)');
|
||||
}));
|
||||
|
||||
it('should allow for the position constrain logic to be customized', fakeAsync(() => {
|
||||
const fixture = createComponent(StandaloneDraggable);
|
||||
const spy = jasmine.createSpy('constrain position spy').and.returnValue({
|
||||
x: 50,
|
||||
y: 50
|
||||
} as Point);
|
||||
|
||||
fixture.componentInstance.constrainPosition = spy;
|
||||
fixture.detectChanges();
|
||||
const dragElement = fixture.componentInstance.dragElement.nativeElement;
|
||||
|
||||
expect(dragElement.style.transform).toBeFalsy();
|
||||
dragElementViaMouse(fixture, dragElement, 300, 300);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({x: 300, y: 300}));
|
||||
expect(dragElement.style.transform).toBe('translate3d(50px, 50px, 0px)');
|
||||
}));
|
||||
|
||||
it('should throw if attached to an ng-container', fakeAsync(() => {
|
||||
expect(() => {
|
||||
createComponent(DraggableOnNgContainer).detectChanges();
|
||||
@@ -2105,6 +2123,33 @@ describe('CdkDrag', () => {
|
||||
expect(Math.floor(previewRect.right)).toBe(Math.floor(listRect.right));
|
||||
}));
|
||||
|
||||
it('should be able to constrain the preview position with a custom function', fakeAsync(() => {
|
||||
const fixture = createComponent(DraggableInDropZoneWithCustomPreview);
|
||||
const spy = jasmine.createSpy('constrain position spy').and.returnValue({
|
||||
x: 50,
|
||||
y: 50
|
||||
} as Point);
|
||||
|
||||
fixture.componentInstance.constrainPosition = spy;
|
||||
fixture.detectChanges();
|
||||
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
|
||||
|
||||
startDraggingViaMouse(fixture, item);
|
||||
|
||||
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
|
||||
|
||||
startDraggingViaMouse(fixture, item, 200, 200);
|
||||
flush();
|
||||
dispatchMouseEvent(document, 'mousemove', 200, 200);
|
||||
fixture.detectChanges();
|
||||
|
||||
const previewRect = preview.getBoundingClientRect();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({x: 200, y: 200}));
|
||||
expect(Math.floor(previewRect.top)).toBe(50);
|
||||
expect(Math.floor(previewRect.left)).toBe(50);
|
||||
}));
|
||||
|
||||
it('should revert the element back to its parent after dragging with a custom ' +
|
||||
'preview has stopped', fakeAsync(() => {
|
||||
const fixture = createComponent(DraggableInDropZoneWithCustomPreview);
|
||||
@@ -3049,6 +3094,7 @@ describe('CdkDrag', () => {
|
||||
cdkDrag
|
||||
[cdkDragBoundary]="boundarySelector"
|
||||
[cdkDragStartDelay]="dragStartDelay"
|
||||
[cdkDragConstrainPosition]="constrainPosition"
|
||||
(cdkDragStarted)="startedSpy($event)"
|
||||
(cdkDragReleased)="releasedSpy($event)"
|
||||
(cdkDragEnded)="endedSpy($event)"
|
||||
@@ -3065,6 +3111,7 @@ class StandaloneDraggable {
|
||||
releasedSpy = jasmine.createSpy('released spy');
|
||||
boundarySelector: string;
|
||||
dragStartDelay: number;
|
||||
constrainPosition: (point: Point) => Point;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -3263,6 +3310,7 @@ class DraggableInHorizontalDropZone {
|
||||
<div
|
||||
*ngFor="let item of items"
|
||||
cdkDrag
|
||||
[cdkDragConstrainPosition]="constrainPosition"
|
||||
[cdkDragBoundary]="boundarySelector"
|
||||
style="width: 100%; height: ${ITEM_HEIGHT}px; background: red;">
|
||||
{{item}}
|
||||
@@ -3283,6 +3331,7 @@ class DraggableInDropZoneWithCustomPreview {
|
||||
items = ['Zero', 'One', 'Two', 'Three'];
|
||||
boundarySelector: string;
|
||||
renderCustomPreview = true;
|
||||
constrainPosition: (point: Point) => Point;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import {CdkDragPlaceholder} from './drag-placeholder';
|
||||
import {CdkDragPreview} from './drag-preview';
|
||||
import {CDK_DROP_LIST} from '../drop-list-container';
|
||||
import {CDK_DRAG_PARENT} from '../drag-parent';
|
||||
import {DragRef, DragRefConfig} from '../drag-ref';
|
||||
import {DragRef, DragRefConfig, Point} from '../drag-ref';
|
||||
import {DropListRef} from '../drop-list-ref';
|
||||
import {CdkDropListInternal as CdkDropList} from './drop-list';
|
||||
import {DragDrop} from '../drag-drop';
|
||||
@@ -127,6 +127,14 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
|
||||
}
|
||||
private _disabled = false;
|
||||
|
||||
/**
|
||||
* Function that can be used to customize the logic of how the position of the drag item
|
||||
* is limited while it's being dragged. Gets called with a point containing the current position
|
||||
* of the user's pointer on the page and should return a point describing where the item should
|
||||
* be rendered.
|
||||
*/
|
||||
@Input('cdkDragConstrainPosition') constrainPosition?: (point: Point) => Point;
|
||||
|
||||
/** Emits when the user starts dragging the item. */
|
||||
@Output('cdkDragStarted') started: EventEmitter<CdkDragStart> = new EventEmitter<CdkDragStart>();
|
||||
|
||||
@@ -310,6 +318,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
|
||||
ref.disabled = this.disabled;
|
||||
ref.lockAxis = this.lockAxis;
|
||||
ref.dragStartDelay = this.dragStartDelay;
|
||||
ref.constrainPosition = this.constrainPosition;
|
||||
ref
|
||||
.withBoundaryElement(this._getBoundaryElement())
|
||||
.withPlaceholderTemplate(placeholder)
|
||||
|
||||
@@ -273,6 +273,14 @@ export class DragRef<T = any> {
|
||||
/** Arbitrary data that can be attached to the drag item. */
|
||||
data: T;
|
||||
|
||||
/**
|
||||
* Function that can be used to customize the logic of how the position of the drag item
|
||||
* is limited while it's being dragged. Gets called with a point containing the current position
|
||||
* of the user's pointer on the page and should return a point describing where the item should
|
||||
* be rendered.
|
||||
*/
|
||||
constrainPosition?: (point: Point) => Point;
|
||||
|
||||
constructor(
|
||||
element: ElementRef<HTMLElement> | HTMLElement,
|
||||
private _config: DragRefConfig,
|
||||
@@ -909,12 +917,13 @@ export class DragRef<T = any> {
|
||||
/** Gets the pointer position on the page, accounting for any position constraints. */
|
||||
private _getConstrainedPointerPosition(event: MouseEvent | TouchEvent): Point {
|
||||
const point = this._getPointerPositionOnPage(event);
|
||||
const constrainedPoint = this.constrainPosition ? this.constrainPosition(point) : point;
|
||||
const dropContainerLock = this._dropContainer ? this._dropContainer.lockAxis : null;
|
||||
|
||||
if (this.lockAxis === 'x' || dropContainerLock === 'x') {
|
||||
point.y = this._pickupPositionOnPage.y;
|
||||
constrainedPoint.y = this._pickupPositionOnPage.y;
|
||||
} else if (this.lockAxis === 'y' || dropContainerLock === 'y') {
|
||||
point.x = this._pickupPositionOnPage.x;
|
||||
constrainedPoint.x = this._pickupPositionOnPage.x;
|
||||
}
|
||||
|
||||
if (this._boundaryRect) {
|
||||
@@ -926,11 +935,11 @@ export class DragRef<T = any> {
|
||||
const minX = boundaryRect.left + pickupX;
|
||||
const maxX = boundaryRect.right - (previewRect.width - pickupX);
|
||||
|
||||
point.x = clamp(point.x, minX, maxX);
|
||||
point.y = clamp(point.y, minY, maxY);
|
||||
constrainedPoint.x = clamp(constrainedPoint.x, minX, maxX);
|
||||
constrainedPoint.y = clamp(constrainedPoint.y, minY, maxY);
|
||||
}
|
||||
|
||||
return point;
|
||||
return constrainedPoint;
|
||||
}
|
||||
|
||||
|
||||
@@ -984,7 +993,7 @@ export class DragRef<T = any> {
|
||||
}
|
||||
|
||||
/** Point on the page or within an element. */
|
||||
interface Point {
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ export declare class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDes
|
||||
_placeholderTemplate: CdkDragPlaceholder;
|
||||
_previewTemplate: CdkDragPreview;
|
||||
boundaryElementSelector: string;
|
||||
constrainPosition?: (point: Point) => Point;
|
||||
data: T;
|
||||
disabled: boolean;
|
||||
dragStartDelay: number;
|
||||
@@ -204,6 +205,7 @@ export declare class DragDropRegistry<I, C extends {
|
||||
|
||||
export declare class DragRef<T = any> {
|
||||
beforeStarted: Subject<void>;
|
||||
constrainPosition?: (point: Point) => Point;
|
||||
data: T;
|
||||
disabled: boolean;
|
||||
dragStartDelay: number;
|
||||
|
||||
Reference in New Issue
Block a user