Skip to content

Commit

Permalink
feat(drag-drop): add injection token for configuring the input defaul…
Browse files Browse the repository at this point in the history
…ts (#17970)

Adds an injection token that allows consumers to change the defaults of the various options in the `drag-drop` module. Also moves some repeated inline types into shared ones.

Fixes #17921.
  • Loading branch information
crisbeto authored and jelbourn committed Jan 24, 2020
1 parent 4e4e0e8 commit 4667cd4
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 43 deletions.
54 changes: 54 additions & 0 deletions src/cdk/drag-drop/directives/config.ts
Original file line number Diff line number Diff line change
@@ -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<DragDropConfig>('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<DragRefConfig> {
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};
}
70 changes: 67 additions & 3 deletions src/cdk/drag-drop/directives/drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -58,7 +59,7 @@ describe('CdkDrag', () => {
// have to deal with thresholds.
dragStartThreshold: dragDistance,
pointerDirectionChangeThreshold: 5
} as DragRefConfig
} as DragDropConfig
},
...providers
],
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -5345,6 +5395,20 @@ class NestedDropZones {
items = ['Zero', 'One', 'Two', 'Three'];
}

@Component({
template: `<div cdkDrag></div>`
})
class PlainStandaloneDraggable {
@ViewChild(CdkDrag) dragInstance: CdkDrag;
}

@Component({
template: `<div cdkDropList></div>`
})
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.
Expand Down
66 changes: 49 additions & 17 deletions src/cdk/drag-drop/directives/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,17 @@ 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.
* Used for avoiding circular imports.
*/
export const CDK_DROP_LIST = new InjectionToken<CdkDropList>('CDK_DROP_LIST');

/** Injection token that can be used to configure the behavior of `CdkDrag`. */
export const CDK_DRAG_CONFIG = new InjectionToken<DragRefConfig>('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]',
Expand Down Expand Up @@ -102,7 +92,7 @@ export class CdkDrag<T = any> 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
Expand All @@ -123,7 +113,7 @@ export class CdkDrag<T = any> 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.
Expand All @@ -140,7 +130,7 @@ export class CdkDrag<T = any> 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
Expand Down Expand Up @@ -200,11 +190,22 @@ export class CdkDrag<T = any> 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);
}
Expand Down Expand Up @@ -414,6 +415,37 @@ export class CdkDrag<T = any> 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;
}

Expand Down
36 changes: 30 additions & 6 deletions src/cdk/drag-drop/directives/drop-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ChangeDetectorRef,
SkipSelf,
AfterContentInit,
Inject,
} from '@angular/core';
import {Directionality} from '@angular/cdk/bidi';
import {ScrollDispatcher} from '@angular/cdk/scrolling';
Expand All @@ -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';

Expand Down Expand Up @@ -84,7 +86,7 @@ export class CdkDropList<T = any> 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
Expand All @@ -93,7 +95,7 @@ export class CdkDropList<T = any> 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')
Expand All @@ -107,11 +109,11 @@ export class CdkDropList<T = any> 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
Expand All @@ -122,7 +124,7 @@ export class CdkDropList<T = any> 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')
Expand Down Expand Up @@ -155,9 +157,15 @@ export class CdkDropList<T = any> 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<CdkDrag>, drop: DropListRef<CdkDropList>) => {
return this.enterPredicate(drag.data, drop.data);
};
Expand Down Expand Up @@ -347,6 +355,22 @@ export class CdkDropList<T = any> 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;
Expand Down
Loading

0 comments on commit 4667cd4

Please sign in to comment.