Skip to content

Commit 4667cd4

Browse files
crisbetojelbourn
authored andcommitted
feat(drag-drop): add injection token for configuring the input defaults (#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.
1 parent 4e4e0e8 commit 4667cd4

File tree

7 files changed

+239
-43
lines changed

7 files changed

+239
-43
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {InjectionToken} from '@angular/core';
10+
import {DragRefConfig, Point, DragRef} from '../drag-ref';
11+
12+
/** Possible values that can be used to configure the drag start delay. */
13+
export type DragStartDelay = number | {touch: number, mouse: number};
14+
15+
/** Possible axis along which dragging can be locked. */
16+
export type DragAxis = 'x' | 'y';
17+
18+
/** Function that can be used to constrain the position of a dragged element. */
19+
export type DragConstrainPosition = (point: Point, dragRef: DragRef) => Point;
20+
21+
/** Possible orientations for a drop list. */
22+
export type DropListOrientation = 'horizontal' | 'vertical';
23+
24+
/**
25+
* Injection token that can be used to configure the
26+
* behavior of the drag&drop-related components.
27+
*/
28+
export const CDK_DRAG_CONFIG = new InjectionToken<DragDropConfig>('CDK_DRAG_CONFIG');
29+
30+
/**
31+
* Object that can be used to configure the drag
32+
* items and drop lists within a module or a component.
33+
*/
34+
export interface DragDropConfig extends Partial<DragRefConfig> {
35+
lockAxis?: DragAxis;
36+
dragStartDelay?: DragStartDelay;
37+
constrainPosition?: DragConstrainPosition;
38+
previewClass?: string | string[];
39+
boundaryElement?: string;
40+
rootElementSelector?: string;
41+
draggingDisabled?: boolean;
42+
sortingDisabled?: boolean;
43+
listAutoScrollDisabled?: boolean;
44+
listOrientation?: DropListOrientation;
45+
}
46+
47+
/**
48+
* @deprecated No longer being used. To be removed.
49+
* @breaking-change 10.0.0
50+
* @docs-private
51+
*/
52+
export function CDK_DRAG_CONFIG_FACTORY(): DragDropConfig {
53+
return {dragStartThreshold: 5, pointerDirectionChangeThreshold: 5};
54+
}

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ import {of as observableOf} from 'rxjs';
2929

3030
import {DragDropModule} from '../drag-drop-module';
3131
import {CdkDragDrop, CdkDragEnter} from '../drag-events';
32-
import {DragRefConfig, Point, DragRef} from '../drag-ref';
32+
import {Point, DragRef} from '../drag-ref';
3333
import {extendStyles} from '../drag-styling';
3434
import {moveItemInArray} from '../drag-utils';
3535

36-
import {CDK_DRAG_CONFIG, CdkDrag} from './drag';
36+
import {CdkDrag} from './drag';
37+
import {CDK_DRAG_CONFIG, DragDropConfig} from './config';
3738
import {CdkDragHandle} from './drag-handle';
3839
import {CdkDropList} from './drop-list';
3940
import {CdkDropListGroup} from './drop-list-group';
@@ -58,7 +59,7 @@ describe('CdkDrag', () => {
5859
// have to deal with thresholds.
5960
dragStartThreshold: dragDistance,
6061
pointerDirectionChangeThreshold: 5
61-
} as DragRefConfig
62+
} as DragDropConfig
6263
},
6364
...providers
6465
],
@@ -1160,6 +1161,32 @@ describe('CdkDrag', () => {
11601161
expect(touchmoveEvent.defaultPrevented).toBe(true);
11611162
}));
11621163

1164+
it('should be able to configure the drag input defaults through a provider', fakeAsync(() => {
1165+
const config: DragDropConfig = {
1166+
draggingDisabled: true,
1167+
dragStartDelay: 1337,
1168+
lockAxis: 'y',
1169+
constrainPosition: () => ({x: 1337, y: 42}),
1170+
previewClass: 'custom-preview-class',
1171+
boundaryElement: '.boundary',
1172+
rootElementSelector: '.root'
1173+
};
1174+
1175+
const fixture = createComponent(PlainStandaloneDraggable, [{
1176+
provide: CDK_DRAG_CONFIG,
1177+
useValue: config
1178+
}]);
1179+
fixture.detectChanges();
1180+
const drag = fixture.componentInstance.dragInstance;
1181+
expect(drag.disabled).toBe(true);
1182+
expect(drag.dragStartDelay).toBe(1337);
1183+
expect(drag.lockAxis).toBe('y');
1184+
expect(drag.constrainPosition).toBe(config.constrainPosition);
1185+
expect(drag.previewClass).toBe('custom-preview-class');
1186+
expect(drag.boundaryElement).toBe('.boundary');
1187+
expect(drag.rootElementSelector).toBe('.root');
1188+
}));
1189+
11631190
});
11641191

11651192
describe('draggable with a handle', () => {
@@ -3510,6 +3537,29 @@ describe('CdkDrag', () => {
35103537
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
35113538
.toEqual(['One', 'Two', 'Zero', 'Three']);
35123539
}));
3540+
3541+
it('should be able to configure the drop input defaults through a provider', fakeAsync(() => {
3542+
const config: DragDropConfig = {
3543+
draggingDisabled: true,
3544+
sortingDisabled: true,
3545+
listAutoScrollDisabled: true,
3546+
listOrientation: 'horizontal',
3547+
lockAxis: 'y'
3548+
};
3549+
3550+
const fixture = createComponent(PlainStandaloneDropList, [{
3551+
provide: CDK_DRAG_CONFIG,
3552+
useValue: config
3553+
}]);
3554+
fixture.detectChanges();
3555+
const list = fixture.componentInstance.dropList;
3556+
expect(list.disabled).toBe(true);
3557+
expect(list.sortingDisabled).toBe(true);
3558+
expect(list.autoScrollDisabled).toBe(true);
3559+
expect(list.orientation).toBe('horizontal');
3560+
expect(list.lockAxis).toBe('y');
3561+
}));
3562+
35133563
});
35143564

35153565
describe('in a connected drop container', () => {
@@ -5345,6 +5395,20 @@ class NestedDropZones {
53455395
items = ['Zero', 'One', 'Two', 'Three'];
53465396
}
53475397

5398+
@Component({
5399+
template: `<div cdkDrag></div>`
5400+
})
5401+
class PlainStandaloneDraggable {
5402+
@ViewChild(CdkDrag) dragInstance: CdkDrag;
5403+
}
5404+
5405+
@Component({
5406+
template: `<div cdkDropList></div>`
5407+
})
5408+
class PlainStandaloneDropList {
5409+
@ViewChild(CdkDropList) dropList: CdkDropList;
5410+
}
5411+
53485412
/**
53495413
* Drags an element to a position on the page using the mouse.
53505414
* @param fixture Fixture on which to run change detection.

src/cdk/drag-drop/directives/drag.ts

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,27 +51,17 @@ import {CdkDragHandle} from './drag-handle';
5151
import {CdkDragPlaceholder} from './drag-placeholder';
5252
import {CdkDragPreview} from './drag-preview';
5353
import {CDK_DRAG_PARENT} from '../drag-parent';
54-
import {DragRef, DragRefConfig, Point} from '../drag-ref';
54+
import {DragRef, Point} from '../drag-ref';
5555
import {CdkDropListInternal as CdkDropList} from './drop-list';
5656
import {DragDrop} from '../drag-drop';
57+
import {CDK_DRAG_CONFIG, DragDropConfig, DragStartDelay, DragAxis} from './config';
5758

5859
/**
5960
* Injection token that is used to provide a CdkDropList instance to CdkDrag.
6061
* Used for avoiding circular imports.
6162
*/
6263
export const CDK_DROP_LIST = new InjectionToken<CdkDropList>('CDK_DROP_LIST');
6364

64-
/** Injection token that can be used to configure the behavior of `CdkDrag`. */
65-
export const CDK_DRAG_CONFIG = new InjectionToken<DragRefConfig>('CDK_DRAG_CONFIG', {
66-
providedIn: 'root',
67-
factory: CDK_DRAG_CONFIG_FACTORY
68-
});
69-
70-
/** @docs-private */
71-
export function CDK_DRAG_CONFIG_FACTORY(): DragRefConfig {
72-
return {dragStartThreshold: 5, pointerDirectionChangeThreshold: 5};
73-
}
74-
7565
/** Element that can be moved inside a CdkDropList container. */
7666
@Directive({
7767
selector: '[cdkDrag]',
@@ -102,7 +92,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
10292
@Input('cdkDragData') data: T;
10393

10494
/** Locks the position of the dragged element along the specified axis. */
105-
@Input('cdkDragLockAxis') lockAxis: 'x' | 'y';
95+
@Input('cdkDragLockAxis') lockAxis: DragAxis;
10696

10797
/**
10898
* Selector that will be used to determine the root draggable element, starting from
@@ -123,7 +113,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
123113
* Amount of milliseconds to wait after the user has put their
124114
* pointer down before starting to drag the element.
125115
*/
126-
@Input('cdkDragStartDelay') dragStartDelay: number | {touch: number, mouse: number} = 0;
116+
@Input('cdkDragStartDelay') dragStartDelay: DragStartDelay;
127117

128118
/**
129119
* Sets the position of a `CdkDrag` that is outside of a drop container.
@@ -140,7 +130,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
140130
this._disabled = coerceBooleanProperty(value);
141131
this._dragRef.disabled = this._disabled;
142132
}
143-
private _disabled = false;
133+
private _disabled: boolean;
144134

145135
/**
146136
* Function that can be used to customize the logic of how the position of the drag item
@@ -200,11 +190,22 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
200190
/** Droppable container that the draggable is a part of. */
201191
@Inject(CDK_DROP_LIST) @Optional() @SkipSelf() public dropContainer: CdkDropList,
202192
@Inject(DOCUMENT) private _document: any, private _ngZone: NgZone,
203-
private _viewContainerRef: ViewContainerRef, @Inject(CDK_DRAG_CONFIG) config: DragRefConfig,
193+
private _viewContainerRef: ViewContainerRef,
194+
@Optional() @Inject(CDK_DRAG_CONFIG) config: DragDropConfig,
204195
@Optional() private _dir: Directionality, dragDrop: DragDrop,
205196
private _changeDetectorRef: ChangeDetectorRef) {
206-
this._dragRef = dragDrop.createDrag(element, config);
197+
this._dragRef = dragDrop.createDrag(element, {
198+
dragStartThreshold: config && config.dragStartThreshold != null ?
199+
config.dragStartThreshold : 5,
200+
pointerDirectionChangeThreshold: config && config.pointerDirectionChangeThreshold != null ?
201+
config.pointerDirectionChangeThreshold : 5
202+
});
207203
this._dragRef.data = this;
204+
205+
if (config) {
206+
this._assignDefaults(config);
207+
}
208+
208209
this._syncInputs(this._dragRef);
209210
this._handleEvents(this._dragRef);
210211
}
@@ -414,6 +415,37 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
414415
});
415416
}
416417

418+
/** Assigns the default input values based on a provided config object. */
419+
private _assignDefaults(config: DragDropConfig) {
420+
const {
421+
lockAxis, dragStartDelay, constrainPosition, previewClass,
422+
boundaryElement, draggingDisabled, rootElementSelector
423+
} = config;
424+
425+
this.disabled = draggingDisabled == null ? false : draggingDisabled;
426+
this.dragStartDelay = dragStartDelay || 0;
427+
428+
if (lockAxis) {
429+
this.lockAxis = lockAxis;
430+
}
431+
432+
if (constrainPosition) {
433+
this.constrainPosition = constrainPosition;
434+
}
435+
436+
if (previewClass) {
437+
this.previewClass = previewClass;
438+
}
439+
440+
if (boundaryElement) {
441+
this.boundaryElement = boundaryElement;
442+
}
443+
444+
if (rootElementSelector) {
445+
this.rootElementSelector = rootElementSelector;
446+
}
447+
}
448+
417449
static ngAcceptInputType_disabled: BooleanInput;
418450
}
419451

src/cdk/drag-drop/directives/drop-list.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
ChangeDetectorRef,
2121
SkipSelf,
2222
AfterContentInit,
23+
Inject,
2324
} from '@angular/core';
2425
import {Directionality} from '@angular/cdk/bidi';
2526
import {ScrollDispatcher} from '@angular/cdk/scrolling';
@@ -29,6 +30,7 @@ import {CdkDropListGroup} from './drop-list-group';
2930
import {DropListRef} from '../drop-list-ref';
3031
import {DragRef} from '../drag-ref';
3132
import {DragDrop} from '../drag-drop';
33+
import {DropListOrientation, DragAxis, DragDropConfig, CDK_DRAG_CONFIG} from './config';
3234
import {Subject} from 'rxjs';
3335
import {startWith, takeUntil} from 'rxjs/operators';
3436

@@ -84,7 +86,7 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
8486
@Input('cdkDropListData') data: T;
8587

8688
/** Direction in which the list is oriented. */
87-
@Input('cdkDropListOrientation') orientation: 'horizontal' | 'vertical' = 'vertical';
89+
@Input('cdkDropListOrientation') orientation: DropListOrientation;
8890

8991
/**
9092
* Unique ID for the drop zone. Can be used as a reference
@@ -93,7 +95,7 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
9395
@Input() id: string = `cdk-drop-list-${_uniqueIdCounter++}`;
9496

9597
/** Locks the position of the draggable elements inside the container along the specified axis. */
96-
@Input('cdkDropListLockAxis') lockAxis: 'x' | 'y';
98+
@Input('cdkDropListLockAxis') lockAxis: DragAxis;
9799

98100
/** Whether starting a dragging sequence from this container is disabled. */
99101
@Input('cdkDropListDisabled')
@@ -107,11 +109,11 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
107109
// the user in a disabled state, so we also need to sync it as it's being set.
108110
this._dropListRef.disabled = this._disabled = coerceBooleanProperty(value);
109111
}
110-
private _disabled = false;
112+
private _disabled: boolean;
111113

112114
/** Whether sorting within this drop list is disabled. */
113115
@Input('cdkDropListSortingDisabled')
114-
sortingDisabled: boolean = false;
116+
sortingDisabled: boolean;
115117

116118
/**
117119
* Function that is used to determine whether an item
@@ -122,7 +124,7 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
122124

123125
/** Whether to auto-scroll the view when the user moves their pointer close to the edges. */
124126
@Input('cdkDropListAutoScrollDisabled')
125-
autoScrollDisabled: boolean = false;
127+
autoScrollDisabled: boolean;
126128

127129
/** Emits when the user drops an item inside the container. */
128130
@Output('cdkDropListDropped')
@@ -155,9 +157,15 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
155157
* @deprecated _scrollDispatcher parameter to become required.
156158
* @breaking-change 11.0.0
157159
*/
158-
private _scrollDispatcher?: ScrollDispatcher) {
160+
private _scrollDispatcher?: ScrollDispatcher,
161+
@Optional() @Inject(CDK_DRAG_CONFIG) config?: DragDropConfig) {
159162
this._dropListRef = dragDrop.createDropList(element);
160163
this._dropListRef.data = this;
164+
165+
if (config) {
166+
this._assignDefaults(config);
167+
}
168+
161169
this._dropListRef.enterPredicate = (drag: DragRef<CdkDrag>, drop: DropListRef<CdkDropList>) => {
162170
return this.enterPredicate(drag.data, drop.data);
163171
};
@@ -347,6 +355,22 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
347355
});
348356
}
349357

358+
/** Assigns the default input values based on a provided config object. */
359+
private _assignDefaults(config: DragDropConfig) {
360+
const {
361+
lockAxis, draggingDisabled, sortingDisabled, listAutoScrollDisabled, listOrientation
362+
} = config;
363+
364+
this.disabled = draggingDisabled == null ? false : draggingDisabled;
365+
this.sortingDisabled = sortingDisabled == null ? false : sortingDisabled;
366+
this.autoScrollDisabled = listAutoScrollDisabled == null ? false : listAutoScrollDisabled;
367+
this.orientation = listOrientation || 'vertical';
368+
369+
if (lockAxis) {
370+
this.lockAxis = lockAxis;
371+
}
372+
}
373+
350374
static ngAcceptInputType_disabled: BooleanInput;
351375
static ngAcceptInputType_sortingDisabled: BooleanInput;
352376
static ngAcceptInputType_autoScrollDisabled: BooleanInput;

0 commit comments

Comments
 (0)