Skip to content

Commit a6054c4

Browse files
committed
feat(drag-drop): add injection token for configuring the input defaults
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 527f1b5 commit a6054c4

File tree

7 files changed

+241
-43
lines changed

7 files changed

+241
-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
],
@@ -1132,6 +1133,32 @@ describe('CdkDrag', () => {
11321133
subscription.unsubscribe();
11331134
}));
11341135

1136+
it('should be able to configure the drag input defaults through a provider', fakeAsync(() => {
1137+
const config: DragDropConfig = {
1138+
draggingDisabled: true,
1139+
dragStartDelay: 1337,
1140+
lockAxis: 'y',
1141+
constrainPosition: () => ({x: 1337, y: 42}),
1142+
previewClass: 'custom-preview-class',
1143+
boundaryElement: '.boundary',
1144+
rootElementSelector: '.root'
1145+
};
1146+
1147+
const fixture = createComponent(PlainStandaloneDraggable, [{
1148+
provide: CDK_DRAG_CONFIG,
1149+
useValue: config
1150+
}]);
1151+
fixture.detectChanges();
1152+
const drag = fixture.componentInstance.dragInstance;
1153+
expect(drag.disabled).toBe(true);
1154+
expect(drag.dragStartDelay).toBe(1337);
1155+
expect(drag.lockAxis).toBe('y');
1156+
expect(drag.constrainPosition).toBe(config.constrainPosition);
1157+
expect(drag.previewClass).toBe('custom-preview-class');
1158+
expect(drag.boundaryElement).toBe('.boundary');
1159+
expect(drag.rootElementSelector).toBe('.root');
1160+
}));
1161+
11351162
});
11361163

11371164
describe('draggable with a handle', () => {
@@ -3456,6 +3483,29 @@ describe('CdkDrag', () => {
34563483
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
34573484
.toEqual(['One', 'Two', 'Zero', 'Three']);
34583485
}));
3486+
3487+
it('should be able to configure the drop input defaults through a provider', fakeAsync(() => {
3488+
const config: DragDropConfig = {
3489+
draggingDisabled: true,
3490+
sortingDisabled: true,
3491+
listAutoScrollDisabled: true,
3492+
listOrientation: 'horizontal',
3493+
lockAxis: 'y'
3494+
};
3495+
3496+
const fixture = createComponent(PlainStandaloneDropList, [{
3497+
provide: CDK_DRAG_CONFIG,
3498+
useValue: config
3499+
}]);
3500+
fixture.detectChanges();
3501+
const list = fixture.componentInstance.dropList;
3502+
expect(list.disabled).toBe(true);
3503+
expect(list.sortingDisabled).toBe(true);
3504+
expect(list.autoScrollDisabled).toBe(true);
3505+
expect(list.orientation).toBe('horizontal');
3506+
expect(list.lockAxis).toBe('y');
3507+
}));
3508+
34593509
});
34603510

34613511
describe('in a connected drop container', () => {
@@ -5267,6 +5317,20 @@ class NestedDropZones {
52675317
items = ['Zero', 'One', 'Two', 'Three'];
52685318
}
52695319

5320+
@Component({
5321+
template: `<div cdkDrag></div>`
5322+
})
5323+
class PlainStandaloneDraggable {
5324+
@ViewChild(CdkDrag) dragInstance: CdkDrag;
5325+
}
5326+
5327+
@Component({
5328+
template: `<div cdkDropList></div>`
5329+
})
5330+
class PlainStandaloneDropList {
5331+
@ViewChild(CdkDropList) dropList: CdkDropList;
5332+
}
5333+
52705334
/**
52715335
* Drags an element to a position on the page using the mouse.
52725336
* @param fixture Fixture on which to run change detection.

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

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,17 @@ import {CdkDragHandle} from './drag-handle';
4646
import {CdkDragPlaceholder} from './drag-placeholder';
4747
import {CdkDragPreview} from './drag-preview';
4848
import {CDK_DRAG_PARENT} from '../drag-parent';
49-
import {DragRef, DragRefConfig, Point} from '../drag-ref';
49+
import {DragRef, Point} from '../drag-ref';
5050
import {CdkDropListInternal as CdkDropList} from './drop-list';
5151
import {DragDrop} from '../drag-drop';
52+
import {CDK_DRAG_CONFIG, DragDropConfig, DragStartDelay, DragAxis} from './config';
5253

5354
/**
5455
* Injection token that is used to provide a CdkDropList instance to CdkDrag.
5556
* Used for avoiding circular imports.
5657
*/
5758
export const CDK_DROP_LIST = new InjectionToken<CdkDropList>('CDK_DROP_LIST');
5859

59-
/** Injection token that can be used to configure the behavior of `CdkDrag`. */
60-
export const CDK_DRAG_CONFIG = new InjectionToken<DragRefConfig>('CDK_DRAG_CONFIG', {
61-
providedIn: 'root',
62-
factory: CDK_DRAG_CONFIG_FACTORY
63-
});
64-
65-
/** @docs-private */
66-
export function CDK_DRAG_CONFIG_FACTORY(): DragRefConfig {
67-
return {dragStartThreshold: 5, pointerDirectionChangeThreshold: 5};
68-
}
69-
7060
/** Element that can be moved inside a CdkDropList container. */
7161
@Directive({
7262
selector: '[cdkDrag]',
@@ -97,7 +87,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
9787
@Input('cdkDragData') data: T;
9888

9989
/** Locks the position of the dragged element along the specified axis. */
100-
@Input('cdkDragLockAxis') lockAxis: 'x' | 'y';
90+
@Input('cdkDragLockAxis') lockAxis: DragAxis;
10191

10292
/**
10393
* Selector that will be used to determine the root draggable element, starting from
@@ -118,7 +108,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
118108
* Amount of milliseconds to wait after the user has put their
119109
* pointer down before starting to drag the element.
120110
*/
121-
@Input('cdkDragStartDelay') dragStartDelay: number | {touch: number, mouse: number} = 0;
111+
@Input('cdkDragStartDelay') dragStartDelay: DragStartDelay;
122112

123113
/**
124114
* Sets the position of a `CdkDrag` that is outside of a drop container.
@@ -135,7 +125,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
135125
this._disabled = coerceBooleanProperty(value);
136126
this._dragRef.disabled = this._disabled;
137127
}
138-
private _disabled = false;
128+
private _disabled: boolean;
139129

140130
/**
141131
* Function that can be used to customize the logic of how the position of the drag item
@@ -195,11 +185,22 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
195185
/** Droppable container that the draggable is a part of. */
196186
@Inject(CDK_DROP_LIST) @Optional() @SkipSelf() public dropContainer: CdkDropList,
197187
@Inject(DOCUMENT) private _document: any, private _ngZone: NgZone,
198-
private _viewContainerRef: ViewContainerRef, @Inject(CDK_DRAG_CONFIG) config: DragRefConfig,
188+
private _viewContainerRef: ViewContainerRef,
189+
@Optional() @Inject(CDK_DRAG_CONFIG) config: DragDropConfig,
199190
@Optional() private _dir: Directionality, dragDrop: DragDrop,
200191
private _changeDetectorRef: ChangeDetectorRef) {
201-
this._dragRef = dragDrop.createDrag(element, config);
192+
this._dragRef = dragDrop.createDrag(element, {
193+
dragStartThreshold: config && config.dragStartThreshold != null ?
194+
config.dragStartThreshold : 5,
195+
pointerDirectionChangeThreshold: config && config.pointerDirectionChangeThreshold != null ?
196+
config.pointerDirectionChangeThreshold : 5
197+
});
202198
this._dragRef.data = this;
199+
200+
if (config) {
201+
this._assignDefaults(config);
202+
}
203+
203204
this._syncInputs(this._dragRef);
204205
this._handleEvents(this._dragRef);
205206
}
@@ -409,6 +410,39 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
409410
});
410411
}
411412

413+
/** Assigns the default input values based on a provided config object. */
414+
private _assignDefaults(config: DragDropConfig) {
415+
const {
416+
lockAxis, dragStartDelay, constrainPosition, previewClass,
417+
boundaryElement, draggingDisabled, rootElementSelector
418+
} = config;
419+
420+
this.disabled = draggingDisabled == null ? false : draggingDisabled;
421+
this.dragStartDelay = dragStartDelay || 0;
422+
423+
console.log(config.dragStartDelay, this.dragStartDelay);
424+
425+
if (lockAxis) {
426+
this.lockAxis = lockAxis;
427+
}
428+
429+
if (constrainPosition) {
430+
this.constrainPosition = constrainPosition;
431+
}
432+
433+
if (previewClass) {
434+
this.previewClass = previewClass;
435+
}
436+
437+
if (boundaryElement) {
438+
this.boundaryElement = boundaryElement;
439+
}
440+
441+
if (rootElementSelector) {
442+
this.rootElementSelector = rootElementSelector;
443+
}
444+
}
445+
412446
static ngAcceptInputType_disabled: boolean | string | null | undefined;
413447
}
414448

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 {CdkDrag, CDK_DROP_LIST} from './drag';
@@ -28,6 +29,7 @@ import {CdkDropListGroup} from './drop-list-group';
2829
import {DropListRef} from '../drop-list-ref';
2930
import {DragRef} from '../drag-ref';
3031
import {DragDrop} from '../drag-drop';
32+
import {DropListOrientation, DragAxis, DragDropConfig, CDK_DRAG_CONFIG} from './config';
3133
import {Subject} from 'rxjs';
3234
import {startWith, takeUntil} from 'rxjs/operators';
3335

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

8587
/** Direction in which the list is oriented. */
86-
@Input('cdkDropListOrientation') orientation: 'horizontal' | 'vertical' = 'vertical';
88+
@Input('cdkDropListOrientation') orientation: DropListOrientation;
8789

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

9496
/** Locks the position of the draggable elements inside the container along the specified axis. */
95-
@Input('cdkDropListLockAxis') lockAxis: 'x' | 'y';
97+
@Input('cdkDropListLockAxis') lockAxis: DragAxis;
9698

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

111113
/** Whether sorting within this drop list is disabled. */
112114
@Input('cdkDropListSortingDisabled')
113-
sortingDisabled: boolean = false;
115+
sortingDisabled: boolean;
114116

115117
/**
116118
* Function that is used to determine whether an item
@@ -121,7 +123,7 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
121123

122124
/** Whether to auto-scroll the view when the user moves their pointer close to the edges. */
123125
@Input('cdkDropListAutoScrollDisabled')
124-
autoScrollDisabled: boolean = false;
126+
autoScrollDisabled: boolean;
125127

126128
/** Emits when the user drops an item inside the container. */
127129
@Output('cdkDropListDropped')
@@ -148,9 +150,15 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
148150
/** Element that the drop list is attached to. */
149151
public element: ElementRef<HTMLElement>, dragDrop: DragDrop,
150152
private _changeDetectorRef: ChangeDetectorRef, @Optional() private _dir?: Directionality,
151-
@Optional() @SkipSelf() private _group?: CdkDropListGroup<CdkDropList>) {
153+
@Optional() @SkipSelf() private _group?: CdkDropListGroup<CdkDropList>,
154+
@Optional() @Inject(CDK_DRAG_CONFIG) config?: DragDropConfig) {
152155
this._dropListRef = dragDrop.createDropList(element);
153156
this._dropListRef.data = this;
157+
158+
if (config) {
159+
this._assignDefaults(config);
160+
}
161+
154162
this._dropListRef.enterPredicate = (drag: DragRef<CdkDrag>, drop: DropListRef<CdkDropList>) => {
155163
return this.enterPredicate(drag.data, drop.data);
156164
};
@@ -332,6 +340,22 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
332340
});
333341
}
334342

343+
/** Assigns the default input values based on a provided config object. */
344+
private _assignDefaults(config: DragDropConfig) {
345+
const {
346+
lockAxis, draggingDisabled, sortingDisabled, listAutoScrollDisabled, listOrientation
347+
} = config;
348+
349+
this.disabled = draggingDisabled == null ? false : draggingDisabled;
350+
this.sortingDisabled = sortingDisabled == null ? false : sortingDisabled;
351+
this.autoScrollDisabled = listAutoScrollDisabled == null ? false : listAutoScrollDisabled;
352+
this.orientation = listOrientation || 'vertical';
353+
354+
if (lockAxis) {
355+
this.lockAxis = lockAxis;
356+
}
357+
}
358+
335359
static ngAcceptInputType_disabled: boolean | string | null | undefined;
336360
static ngAcceptInputType_sortingDisabled: boolean | string | null | undefined;
337361
static ngAcceptInputType_autoScrollDisabled: boolean | string | null | undefined;

0 commit comments

Comments
 (0)