Skip to content

Commit

Permalink
feat(drag-drop): add injection token for configuring the input defaults
Browse files Browse the repository at this point in the history
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 committed Dec 15, 2019
1 parent 527f1b5 commit a6054c4
Show file tree
Hide file tree
Showing 7 changed files with 241 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 @@ -1132,6 +1133,32 @@ describe('CdkDrag', () => {
subscription.unsubscribe();
}));

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 @@ -3456,6 +3483,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 @@ -5267,6 +5317,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
68 changes: 51 additions & 17 deletions src/cdk/drag-drop/directives/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,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 @@ -97,7 +87,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 @@ -118,7 +108,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 @@ -135,7 +125,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 @@ -195,11 +185,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 @@ -409,6 +410,39 @@ 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;

console.log(config.dragStartDelay, this.dragStartDelay);

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: boolean | string | null | undefined;
}

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 {CdkDrag, CDK_DROP_LIST} from './drag';
Expand All @@ -28,6 +29,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 @@ -83,7 +85,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 @@ -92,7 +94,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 @@ -106,11 +108,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 @@ -121,7 +123,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 All @@ -148,9 +150,15 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
/** Element that the drop list is attached to. */
public element: ElementRef<HTMLElement>, dragDrop: DragDrop,
private _changeDetectorRef: ChangeDetectorRef, @Optional() private _dir?: Directionality,
@Optional() @SkipSelf() private _group?: CdkDropListGroup<CdkDropList>) {
@Optional() @SkipSelf() private _group?: CdkDropListGroup<CdkDropList>,
@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 @@ -332,6 +340,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: boolean | string | null | undefined;
static ngAcceptInputType_sortingDisabled: boolean | string | null | undefined;
static ngAcceptInputType_autoScrollDisabled: boolean | string | null | undefined;
Expand Down
Loading

0 comments on commit a6054c4

Please sign in to comment.