diff --git a/src/cdk/drag-drop/directives/config.ts b/src/cdk/drag-drop/directives/config.ts new file mode 100644 index 000000000000..7ab2b217625d --- /dev/null +++ b/src/cdk/drag-drop/directives/config.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {InjectionToken} from '@angular/core'; +import {DragRefConfig, Point, DragRef} from '../drag-ref'; + +/** Possible values that can be used to configure the drag start delay. */ +export type DragStartDelay = number | {touch: number, mouse: number}; + +/** Possible axis along which dragging can be locked. */ +export type DragAxis = 'x' | 'y'; + +/** Function that can be used to constrain the position of a dragged element. */ +export type DragConstrainPosition = (point: Point, dragRef: DragRef) => Point; + +/** Possible orientations for a drop list. */ +export type DropListOrientation = 'horizontal' | 'vertical'; + +/** + * Injection token that can be used to configure the + * behavior of the drag&drop-related components. + */ +export const CDK_DRAG_CONFIG = new InjectionToken('CDK_DRAG_CONFIG'); + +/** + * Object that can be used to configure the drag + * items and drop lists within a module or a component. + */ +export interface DragDropConfig extends Partial { + lockAxis?: DragAxis; + dragStartDelay?: DragStartDelay; + constrainPosition?: DragConstrainPosition; + previewClass?: string | string[]; + boundaryElement?: string; + rootElementSelector?: string; + draggingDisabled?: boolean; + sortingDisabled?: boolean; + listAutoScrollDisabled?: boolean; + listOrientation?: DropListOrientation; +} + +/** + * @deprecated No longer being used. To be removed. + * @breaking-change 10.0.0 + * @docs-private + */ +export function CDK_DRAG_CONFIG_FACTORY(): DragDropConfig { + return {dragStartThreshold: 5, pointerDirectionChangeThreshold: 5}; +} diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index 0c855149c74b..db1356b5be0f 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -29,11 +29,12 @@ import {of as observableOf} from 'rxjs'; import {DragDropModule} from '../drag-drop-module'; import {CdkDragDrop, CdkDragEnter} from '../drag-events'; -import {DragRefConfig, Point, DragRef} from '../drag-ref'; +import {Point, DragRef} from '../drag-ref'; import {extendStyles} from '../drag-styling'; import {moveItemInArray} from '../drag-utils'; -import {CDK_DRAG_CONFIG, CdkDrag} from './drag'; +import {CdkDrag} from './drag'; +import {CDK_DRAG_CONFIG, DragDropConfig} from './config'; import {CdkDragHandle} from './drag-handle'; import {CdkDropList} from './drop-list'; import {CdkDropListGroup} from './drop-list-group'; @@ -58,7 +59,7 @@ describe('CdkDrag', () => { // have to deal with thresholds. dragStartThreshold: dragDistance, pointerDirectionChangeThreshold: 5 - } as DragRefConfig + } as DragDropConfig }, ...providers ], @@ -1160,6 +1161,32 @@ describe('CdkDrag', () => { expect(touchmoveEvent.defaultPrevented).toBe(true); })); + it('should be able to configure the drag input defaults through a provider', fakeAsync(() => { + const config: DragDropConfig = { + draggingDisabled: true, + dragStartDelay: 1337, + lockAxis: 'y', + constrainPosition: () => ({x: 1337, y: 42}), + previewClass: 'custom-preview-class', + boundaryElement: '.boundary', + rootElementSelector: '.root' + }; + + const fixture = createComponent(PlainStandaloneDraggable, [{ + provide: CDK_DRAG_CONFIG, + useValue: config + }]); + fixture.detectChanges(); + const drag = fixture.componentInstance.dragInstance; + expect(drag.disabled).toBe(true); + expect(drag.dragStartDelay).toBe(1337); + expect(drag.lockAxis).toBe('y'); + expect(drag.constrainPosition).toBe(config.constrainPosition); + expect(drag.previewClass).toBe('custom-preview-class'); + expect(drag.boundaryElement).toBe('.boundary'); + expect(drag.rootElementSelector).toBe('.root'); + })); + }); describe('draggable with a handle', () => { @@ -3510,6 +3537,29 @@ describe('CdkDrag', () => { expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim())) .toEqual(['One', 'Two', 'Zero', 'Three']); })); + + it('should be able to configure the drop input defaults through a provider', fakeAsync(() => { + const config: DragDropConfig = { + draggingDisabled: true, + sortingDisabled: true, + listAutoScrollDisabled: true, + listOrientation: 'horizontal', + lockAxis: 'y' + }; + + const fixture = createComponent(PlainStandaloneDropList, [{ + provide: CDK_DRAG_CONFIG, + useValue: config + }]); + fixture.detectChanges(); + const list = fixture.componentInstance.dropList; + expect(list.disabled).toBe(true); + expect(list.sortingDisabled).toBe(true); + expect(list.autoScrollDisabled).toBe(true); + expect(list.orientation).toBe('horizontal'); + expect(list.lockAxis).toBe('y'); + })); + }); describe('in a connected drop container', () => { @@ -5345,6 +5395,20 @@ class NestedDropZones { items = ['Zero', 'One', 'Two', 'Three']; } +@Component({ + template: `
` +}) +class PlainStandaloneDraggable { + @ViewChild(CdkDrag) dragInstance: CdkDrag; +} + +@Component({ + template: `
` +}) +class PlainStandaloneDropList { + @ViewChild(CdkDropList) dropList: CdkDropList; +} + /** * Drags an element to a position on the page using the mouse. * @param fixture Fixture on which to run change detection. diff --git a/src/cdk/drag-drop/directives/drag.ts b/src/cdk/drag-drop/directives/drag.ts index 05ed967b8682..33ff1be267aa 100644 --- a/src/cdk/drag-drop/directives/drag.ts +++ b/src/cdk/drag-drop/directives/drag.ts @@ -51,9 +51,10 @@ import {CdkDragHandle} from './drag-handle'; import {CdkDragPlaceholder} from './drag-placeholder'; import {CdkDragPreview} from './drag-preview'; import {CDK_DRAG_PARENT} from '../drag-parent'; -import {DragRef, DragRefConfig, Point} from '../drag-ref'; +import {DragRef, Point} from '../drag-ref'; import {CdkDropListInternal as CdkDropList} from './drop-list'; import {DragDrop} from '../drag-drop'; +import {CDK_DRAG_CONFIG, DragDropConfig, DragStartDelay, DragAxis} from './config'; /** * Injection token that is used to provide a CdkDropList instance to CdkDrag. @@ -61,17 +62,6 @@ import {DragDrop} from '../drag-drop'; */ export const CDK_DROP_LIST = new InjectionToken('CDK_DROP_LIST'); -/** Injection token that can be used to configure the behavior of `CdkDrag`. */ -export const CDK_DRAG_CONFIG = new InjectionToken('CDK_DRAG_CONFIG', { - providedIn: 'root', - factory: CDK_DRAG_CONFIG_FACTORY -}); - -/** @docs-private */ -export function CDK_DRAG_CONFIG_FACTORY(): DragRefConfig { - return {dragStartThreshold: 5, pointerDirectionChangeThreshold: 5}; -} - /** Element that can be moved inside a CdkDropList container. */ @Directive({ selector: '[cdkDrag]', @@ -102,7 +92,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { @Input('cdkDragData') data: T; /** Locks the position of the dragged element along the specified axis. */ - @Input('cdkDragLockAxis') lockAxis: 'x' | 'y'; + @Input('cdkDragLockAxis') lockAxis: DragAxis; /** * Selector that will be used to determine the root draggable element, starting from @@ -123,7 +113,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { * Amount of milliseconds to wait after the user has put their * pointer down before starting to drag the element. */ - @Input('cdkDragStartDelay') dragStartDelay: number | {touch: number, mouse: number} = 0; + @Input('cdkDragStartDelay') dragStartDelay: DragStartDelay; /** * Sets the position of a `CdkDrag` that is outside of a drop container. @@ -140,7 +130,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { this._disabled = coerceBooleanProperty(value); this._dragRef.disabled = this._disabled; } - private _disabled = false; + private _disabled: boolean; /** * Function that can be used to customize the logic of how the position of the drag item @@ -200,11 +190,22 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { /** Droppable container that the draggable is a part of. */ @Inject(CDK_DROP_LIST) @Optional() @SkipSelf() public dropContainer: CdkDropList, @Inject(DOCUMENT) private _document: any, private _ngZone: NgZone, - private _viewContainerRef: ViewContainerRef, @Inject(CDK_DRAG_CONFIG) config: DragRefConfig, + private _viewContainerRef: ViewContainerRef, + @Optional() @Inject(CDK_DRAG_CONFIG) config: DragDropConfig, @Optional() private _dir: Directionality, dragDrop: DragDrop, private _changeDetectorRef: ChangeDetectorRef) { - this._dragRef = dragDrop.createDrag(element, config); + this._dragRef = dragDrop.createDrag(element, { + dragStartThreshold: config && config.dragStartThreshold != null ? + config.dragStartThreshold : 5, + pointerDirectionChangeThreshold: config && config.pointerDirectionChangeThreshold != null ? + config.pointerDirectionChangeThreshold : 5 + }); this._dragRef.data = this; + + if (config) { + this._assignDefaults(config); + } + this._syncInputs(this._dragRef); this._handleEvents(this._dragRef); } @@ -414,6 +415,37 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { }); } + /** Assigns the default input values based on a provided config object. */ + private _assignDefaults(config: DragDropConfig) { + const { + lockAxis, dragStartDelay, constrainPosition, previewClass, + boundaryElement, draggingDisabled, rootElementSelector + } = config; + + this.disabled = draggingDisabled == null ? false : draggingDisabled; + this.dragStartDelay = dragStartDelay || 0; + + if (lockAxis) { + this.lockAxis = lockAxis; + } + + if (constrainPosition) { + this.constrainPosition = constrainPosition; + } + + if (previewClass) { + this.previewClass = previewClass; + } + + if (boundaryElement) { + this.boundaryElement = boundaryElement; + } + + if (rootElementSelector) { + this.rootElementSelector = rootElementSelector; + } + } + static ngAcceptInputType_disabled: BooleanInput; } diff --git a/src/cdk/drag-drop/directives/drop-list.ts b/src/cdk/drag-drop/directives/drop-list.ts index b7eee425d20b..0c89bb126a08 100644 --- a/src/cdk/drag-drop/directives/drop-list.ts +++ b/src/cdk/drag-drop/directives/drop-list.ts @@ -20,6 +20,7 @@ import { ChangeDetectorRef, SkipSelf, AfterContentInit, + Inject, } from '@angular/core'; import {Directionality} from '@angular/cdk/bidi'; import {ScrollDispatcher} from '@angular/cdk/scrolling'; @@ -29,6 +30,7 @@ import {CdkDropListGroup} from './drop-list-group'; import {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 {Subject} from 'rxjs'; import {startWith, takeUntil} from 'rxjs/operators'; @@ -84,7 +86,7 @@ export class CdkDropList implements AfterContentInit, OnDestroy { @Input('cdkDropListData') data: T; /** Direction in which the list is oriented. */ - @Input('cdkDropListOrientation') orientation: 'horizontal' | 'vertical' = 'vertical'; + @Input('cdkDropListOrientation') orientation: DropListOrientation; /** * Unique ID for the drop zone. Can be used as a reference @@ -93,7 +95,7 @@ export class CdkDropList implements AfterContentInit, OnDestroy { @Input() id: string = `cdk-drop-list-${_uniqueIdCounter++}`; /** Locks the position of the draggable elements inside the container along the specified axis. */ - @Input('cdkDropListLockAxis') lockAxis: 'x' | 'y'; + @Input('cdkDropListLockAxis') lockAxis: DragAxis; /** Whether starting a dragging sequence from this container is disabled. */ @Input('cdkDropListDisabled') @@ -107,11 +109,11 @@ export class CdkDropList implements AfterContentInit, OnDestroy { // the user in a disabled state, so we also need to sync it as it's being set. this._dropListRef.disabled = this._disabled = coerceBooleanProperty(value); } - private _disabled = false; + private _disabled: boolean; /** Whether sorting within this drop list is disabled. */ @Input('cdkDropListSortingDisabled') - sortingDisabled: boolean = false; + sortingDisabled: boolean; /** * Function that is used to determine whether an item @@ -122,7 +124,7 @@ export class CdkDropList implements AfterContentInit, OnDestroy { /** Whether to auto-scroll the view when the user moves their pointer close to the edges. */ @Input('cdkDropListAutoScrollDisabled') - autoScrollDisabled: boolean = false; + autoScrollDisabled: boolean; /** Emits when the user drops an item inside the container. */ @Output('cdkDropListDropped') @@ -155,9 +157,15 @@ export class CdkDropList implements AfterContentInit, OnDestroy { * @deprecated _scrollDispatcher parameter to become required. * @breaking-change 11.0.0 */ - private _scrollDispatcher?: ScrollDispatcher) { + private _scrollDispatcher?: ScrollDispatcher, + @Optional() @Inject(CDK_DRAG_CONFIG) config?: DragDropConfig) { this._dropListRef = dragDrop.createDropList(element); this._dropListRef.data = this; + + if (config) { + this._assignDefaults(config); + } + this._dropListRef.enterPredicate = (drag: DragRef, drop: DropListRef) => { return this.enterPredicate(drag.data, drop.data); }; @@ -347,6 +355,22 @@ export class CdkDropList implements AfterContentInit, OnDestroy { }); } + /** Assigns the default input values based on a provided config object. */ + private _assignDefaults(config: DragDropConfig) { + const { + lockAxis, draggingDisabled, sortingDisabled, listAutoScrollDisabled, listOrientation + } = config; + + this.disabled = draggingDisabled == null ? false : draggingDisabled; + this.sortingDisabled = sortingDisabled == null ? false : sortingDisabled; + this.autoScrollDisabled = listAutoScrollDisabled == null ? false : listAutoScrollDisabled; + this.orientation = listOrientation || 'vertical'; + + if (lockAxis) { + this.lockAxis = lockAxis; + } + } + static ngAcceptInputType_disabled: BooleanInput; static ngAcceptInputType_sortingDisabled: BooleanInput; static ngAcceptInputType_autoScrollDisabled: BooleanInput; diff --git a/src/cdk/drag-drop/drag-ref.ts b/src/cdk/drag-drop/drag-ref.ts index 56f4ebade056..b5c8122a16a1 100644 --- a/src/cdk/drag-drop/drag-ref.ts +++ b/src/cdk/drag-drop/drag-ref.ts @@ -64,6 +64,12 @@ interface DragHelperTemplate { context: T; } +/** Point on the page or within an element. */ +export interface Point { + x: number; + y: number; +} + /** * Reference to a draggable item. Used to manipulate or dispose of the item. */ @@ -1186,12 +1192,6 @@ export class DragRef { } } -/** Point on the page or within an element. */ -export interface Point { - x: number; - y: number; -} - /** * Gets a 3d `transform` that can be applied to an element. * @param x Desired position of the element along the X axis. diff --git a/src/cdk/drag-drop/public-api.ts b/src/cdk/drag-drop/public-api.ts index 18284d498230..c5864b22f418 100644 --- a/src/cdk/drag-drop/public-api.ts +++ b/src/cdk/drag-drop/public-api.ts @@ -16,6 +16,7 @@ export * from './drag-drop-module'; export * from './drag-drop-registry'; export {CdkDropList} from './directives/drop-list'; +export * from './directives/config'; export * from './directives/drop-list-group'; export * from './directives/drag'; export * from './directives/drag-handle'; diff --git a/tools/public_api_guard/cdk/drag-drop.d.ts b/tools/public_api_guard/cdk/drag-drop.d.ts index ab25b625746c..13abcac7c21f 100644 --- a/tools/public_api_guard/cdk/drag-drop.d.ts +++ b/tools/public_api_guard/cdk/drag-drop.d.ts @@ -1,6 +1,6 @@ -export declare const CDK_DRAG_CONFIG: InjectionToken; +export declare const CDK_DRAG_CONFIG: InjectionToken; -export declare function CDK_DRAG_CONFIG_FACTORY(): DragRefConfig; +export declare function CDK_DRAG_CONFIG_FACTORY(): DragDropConfig; export declare const CDK_DROP_LIST: InjectionToken; @@ -14,10 +14,7 @@ export declare class CdkDrag implements AfterViewInit, OnChanges, OnDes data: T; get disabled(): boolean; set disabled(value: boolean); - dragStartDelay: number | { - touch: number; - mouse: number; - }; + dragStartDelay: DragStartDelay; dropContainer: CdkDropList; dropped: EventEmitter>; element: ElementRef; @@ -28,7 +25,7 @@ export declare class CdkDrag implements AfterViewInit, OnChanges, OnDes x: number; y: number; }; - lockAxis: 'x' | 'y'; + lockAxis: DragAxis; moved: Observable>; previewClass: string | string[]; released: EventEmitter; @@ -36,7 +33,7 @@ export declare class CdkDrag implements AfterViewInit, OnChanges, OnDes started: EventEmitter; constructor( element: ElementRef, - dropContainer: CdkDropList, _document: any, _ngZone: NgZone, _viewContainerRef: ViewContainerRef, config: DragRefConfig, _dir: Directionality, dragDrop: DragDrop, _changeDetectorRef: ChangeDetectorRef); + dropContainer: CdkDropList, _document: any, _ngZone: NgZone, _viewContainerRef: ViewContainerRef, config: DragDropConfig, _dir: Directionality, dragDrop: DragDrop, _changeDetectorRef: ChangeDetectorRef); getFreeDragPosition(): { readonly x: number; readonly y: number; @@ -159,13 +156,13 @@ export declare class CdkDropList implements AfterContentInit, OnDestroy entered: EventEmitter>; exited: EventEmitter>; id: string; - lockAxis: 'x' | 'y'; - orientation: 'horizontal' | 'vertical'; + lockAxis: DragAxis; + orientation: DropListOrientation; sorted: EventEmitter>; sortingDisabled: boolean; constructor( element: ElementRef, dragDrop: DragDrop, _changeDetectorRef: ChangeDetectorRef, _dir?: Directionality | undefined, _group?: CdkDropListGroup> | undefined, - _scrollDispatcher?: ScrollDispatcher | undefined); + _scrollDispatcher?: ScrollDispatcher | undefined, config?: DragDropConfig); drop(item: CdkDrag, currentIndex: number, previousContainer: CdkDropList, isPointerOverContainer: boolean): void; enter(item: CdkDrag, pointerX: number, pointerY: number): void; exit(item: CdkDrag): void; @@ -192,6 +189,10 @@ export declare class CdkDropListGroup implements OnDestroy { export declare function copyArrayItem(currentArray: T[], targetArray: T[], currentIndex: number, targetIndex: number): void; +export declare type DragAxis = 'x' | 'y'; + +export declare type DragConstrainPosition = (point: Point, dragRef: DragRef) => Point; + export declare class DragDrop { constructor(_document: any, _ngZone: NgZone, _viewportRuler: ViewportRuler, _dragDropRegistry: DragDropRegistry); createDrag(element: ElementRef | HTMLElement, config?: DragRefConfig): DragRef; @@ -200,6 +201,19 @@ export declare class DragDrop { static ɵprov: i0.ɵɵInjectableDef; } +export interface DragDropConfig extends Partial { + boundaryElement?: string; + constrainPosition?: DragConstrainPosition; + dragStartDelay?: DragStartDelay; + draggingDisabled?: boolean; + listAutoScrollDisabled?: boolean; + listOrientation?: DropListOrientation; + lockAxis?: DragAxis; + previewClass?: string | string[]; + rootElementSelector?: string; + sortingDisabled?: boolean; +} + export declare class DragDropModule { static ɵinj: i0.ɵɵInjectorDef; static ɵmod: i0.ɵɵNgModuleDefWithMeta; @@ -300,6 +314,13 @@ export interface DragRefConfig { pointerDirectionChangeThreshold: number; } +export declare type DragStartDelay = number | { + touch: number; + mouse: number; +}; + +export declare type DropListOrientation = 'horizontal' | 'vertical'; + export declare class DropListRef { autoScrollDisabled: boolean; beforeStarted: Subject;