Skip to content

Commit f3555f1

Browse files
crisbetovivian-hu-zz
authored andcommitted
refactor(drag-drop): convert cdk-drop into a directive (#13441)
Converts `cdk-drop` into a directive and moves the handful of structural styles into JS. Fixes #13341.
1 parent 4b15b78 commit f3555f1

File tree

16 files changed

+201
-151
lines changed

16 files changed

+201
-151
lines changed

src/cdk/drag-drop/BUILD.bazel

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ ng_module(
88
name = "drag-drop",
99
srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]),
1010
module_name = "@angular/cdk/drag-drop",
11-
assets = [":drop.css"],
1211
deps = [
1312
"@rxjs",
1413
"//src/cdk/platform",
@@ -32,11 +31,6 @@ ts_library(
3231
tsconfig = "//src/cdk:tsconfig-build.json",
3332
)
3433

35-
sass_binary(
36-
name = "drop_scss",
37-
src = "drop.scss",
38-
)
39-
4034
ts_web_test(
4135
name = "unit_tests",
4236
bootstrap = [

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,25 +155,15 @@ describe('DragDropRegistry', () => {
155155
expect(dispatchTouchEvent(document, 'touchmove').defaultPrevented).toBe(true);
156156
});
157157

158-
it('should disable the native interactions on the body while dragging', () => {
159-
const firstItem = testComponent.dragItems.first;
160-
161-
registry.startDragging(firstItem, createMouseEvent('mousedown'));
162-
expect(document.body.classList).toContain('cdk-drag-drop-disable-native-interactions');
163-
164-
registry.stopDragging(firstItem);
165-
expect(document.body.classList).not.toContain('cdk-drag-drop-disable-native-interactions');
166-
});
167-
168158
});
169159

170160
@Component({
171161
template: `
172-
<cdk-drop id="items" [data]="items">
162+
<div cdkDrop id="items" [cdkDropData]="items">
173163
<div *ngFor="let item of items" cdkDrag>{{item}}</div>
174-
</cdk-drop>
164+
</div>
175165
176-
<cdk-drop id="items" *ngIf="showDuplicateContainer"></cdk-drop>
166+
<div cdkDrop id="items" *ngIf="showDuplicateContainer"></div>
177167
`
178168
})
179169
class SimpleDropZone {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {Injectable, NgZone, OnDestroy, Inject} from '@angular/core';
1010
import {DOCUMENT} from '@angular/common';
1111
import {supportsPassiveEventListeners} from '@angular/cdk/platform';
1212
import {Subject} from 'rxjs';
13+
import {toggleNativeDragInteractions} from './drag-styling';
1314

1415
/** Event options that can be used to bind an active event. */
1516
const activeEventOptions = supportsPassiveEventListeners() ? {passive: false} : false;
@@ -118,7 +119,7 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
118119

119120
// We need to disable the native interactions on the entire body, because
120121
// the user can start marking text if they drag too far in Safari.
121-
this._document.body.classList.add('cdk-drag-drop-disable-native-interactions');
122+
toggleNativeDragInteractions(this._document.body, false);
122123

123124
// We explicitly bind __active__ listeners here, because newer browsers will default to
124125
// passive ones for `mousemove` and `touchmove`. The events need to be active, because we
@@ -140,7 +141,7 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
140141

141142
if (this._activeDragInstances.size === 0) {
142143
this._clearGlobalListeners();
143-
this._document.body.classList.remove('cdk-drag-drop-disable-native-interactions');
144+
toggleNativeDragInteractions(this._document.body, true);
144145
}
145146
}
146147

src/cdk/drag-drop/drag-drop.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,62 +6,62 @@ in addition to horizontal lists and locking along an axis.
66
### Getting started
77
Start by importing `DragDropModule` into the `NgModule` where you want to use drag-and-drop
88
features. You can now add the `cdkDrag` directive to elements to make them draggable. When
9-
outside of a `<cdk-drop>` element, draggable elements can be freely moved around the page.
10-
You can add `<cdk-drop>` elements to constrain where elements may be dropped.
9+
outside of a `cdkDrop` element, draggable elements can be freely moved around the page.
10+
You can add `cdkDrop` elements to constrain where elements may be dropped.
1111

1212
<!-- example(cdk-drag-drop-overview) -->
1313

1414
### Reordering lists
15-
Adding `<cdk-drop>` around a set of `cdkDrag` elements groups the draggables into a
15+
Adding `cdkDrop` around a set of `cdkDrag` elements groups the draggables into a
1616
reorderable collection. Items will automatically rearrange as an element moves. Note
17-
that this will *not* update your data model; you can listen to the `dropped` event to
17+
that this will *not* update your data model; you can listen to the `cdkDropDropped` event to
1818
update the data model once the user finishes dragging.
1919

2020
<!-- example(cdk-drag-drop-sorting) -->
2121

2222
### Transferring items between lists
23-
The `<cdk-drop>` component supports transferring dragged items between connected drop zones.
24-
You can connect one or more `<cdk-drop>` instances together by setting the `connectedTo`
23+
The `cdkDrop` directive supports transferring dragged items between connected drop zones.
24+
You can connect one or more `cdkDrop` instances together by setting the `cdkDropConnectedTo`
2525
property.
2626

2727
<!-- example(cdk-drag-drop-connected-sorting) -->
2828

29-
Note that `connectedTo` works both with a direct reference to another `<cdk-drop>`, or by
29+
Note that `cdkDropConnectedTo` works both with a direct reference to another `cdkDrop`, or by
3030
referencing the `id` of another drop container:
3131

3232
```html
3333
<!-- This is valid -->
34-
<cdk-drop #listOne [connectedTo]="[listTwo]"></cdk-drop>
35-
<cdk-drop #listTwo [connectedTo]="[listOne]"></cdk-drop>
34+
<div cdkDrop #listOne="cdkDrop" [cdkDropConnectedTo]="[listTwo]"></div>
35+
<div cdkDrop #listTwo="cdkDrop" [cdkDropConnectedTo]="[listOne]"></div>
3636

3737
<!-- This is valid as well -->
38-
<cdk-drop id="list-one" [connectedTo]="['list-two']"></cdk-drop>
39-
<cdk-drop id="list-two" [connectedTo]="['list-one']"></cdk-drop>
38+
<div cdkDrop id="list-one" [cdkDropConnectedTo]="['list-two']"></div>
39+
<div cdkDrop id="list-two" [cdkDropConnectedTo]="['list-one']"></div>
4040
```
4141

4242
### Attaching data
43-
You can associate some arbitrary data with both `cdkDrag` and `<cdk-drop>` by setting
44-
`cdkDragData` or `data`, respectively. Events fired from both directives include this data,
45-
allowing you to easily identify the origin of the drag or drop interaction.
43+
You can associate some arbitrary data with both `cdkDrag` and `cdkDrop` by setting `cdkDragData`
44+
or `cdkDropData`, respectively. Events fired from both directives include this data, allowing
45+
you to easily identify the origin of the drag or drop interaction.
4646

4747
```html
48-
<cdk-drop [data]="list" *ngFor="let list of lists" (dropped)="drop($event)">
48+
<div cdkDrop [cdkDropData]="list" *ngFor="let list of lists" (cdkDropDropped)="drop($event)">
4949
<div cdkDrag [cdkDragData]="item" *ngFor="let item of list"></div>
50-
</cdk-drop>
50+
</div>
5151
```
5252

5353
### Styling
54-
The `cdkDrag` and `<cdk-drop>` directive include only those styles strictly necessary for
54+
The `cdkDrag` and `cdkDrop` directive include only those styles strictly necessary for
5555
functionality. The application can then customize the elements by styling CSS classes added
5656
by the directives:
5757

5858
| Selector | Description |
5959
|---------------------|--------------------------------------------------------------------------|
60-
| `.cdk-drop` | Corresponds to the `<cdk-drop>` container. |
60+
| `.cdk-drop` | Corresponds to the `cdkDrop` container. |
6161
| `.cdk-drag` | Corresponds to a `cdkDrag` instance. |
6262
| `.cdk-drag-preview` | This is the element that will be rendered next to the user's cursor as they're dragging an item in a sortable list. By default the element looks exactly like the element that is being dragged. |
63-
| `.cdk-drag-placeholder` | This is element that will be shown instead of the real element as it's being dragged inside a `<cdk-drop>`. By default this will look exactly like the element that is being sorted. |
64-
| `.cdk-drop-dragging` | A class that is added to `<cdk-drop>` while the user is dragging an item. |
63+
| `.cdk-drag-placeholder` | This is element that will be shown instead of the real element as it's being dragged inside a `cdkDrop`. By default this will look exactly like the element that is being sorted. |
64+
| `.cdk-drop-dragging` | A class that is added to `cdkDrop` while the user is dragging an item. |
6565

6666
### Animations
6767
The drag-and-drop module supports animations both while sorting an element inside a list, as well as
@@ -73,7 +73,7 @@ following classes can be used for animations:
7373
through a list.
7474
* `.cdk-drag-animating` - This class is added to a `cdkDrag` when the user has stopped dragging.
7575
If you add a `transition` to it, the CDK will animate the element from its drop position to
76-
the final position inside the `<cdk-drop>` container.
76+
the final position inside the `cdkDrop` container.
7777

7878
Example animations:
7979

@@ -105,14 +105,14 @@ This preview can be customized, though, by providing a custom template via `*cdk
105105
<!-- example(cdk-drag-drop-custom-preview) -->
106106

107107
### List orientation
108-
The `cdk-drop` component assumes that lists are vertical by default. This can be
108+
The `cdkDrop` directive assumes that lists are vertical by default. This can be
109109
changed by setting the `orientation` property to `"horizontal".
110110

111111
<!-- example(cdk-drag-drop-horizontal-sorting) -->
112112

113113
### Restricting movement along an axis
114114
By default, `cdkDrag` allows free movement in all directions. To restrict dragging to a
115-
specific axis, you can set `cdkDragLockAxis` on `cdkDrag` or `lockAxis` on `<cdk-drop>`
115+
specific axis, you can set `cdkDragLockAxis` on `cdkDrag` or `lockAxis` on `cdkDrop`
116116
to either `"x"` or `"y"`.
117117

118118
<!-- example(cdk-drag-drop-axis-lock) -->

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {Directive, ElementRef} from '@angular/core';
10+
import {toggleNativeDragInteractions} from './drag-styling';
1011

1112
/** Handle that can be used to drag and CdkDrag instance. */
1213
@Directive({
@@ -16,5 +17,7 @@ import {Directive, ElementRef} from '@angular/core';
1617
}
1718
})
1819
export class CdkDragHandle {
19-
constructor(public element: ElementRef<HTMLElement>) {}
20+
constructor(public element: ElementRef<HTMLElement>) {
21+
toggleNativeDragInteractions(element.nativeElement, false);
22+
}
2023
}

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
/**
10+
* Extended CSSStyleDeclaration that includes a couple of drag-related
11+
* properties that aren't in the built-in TS typings.
12+
*/
13+
interface DragCSSStyleDeclaration extends CSSStyleDeclaration {
14+
webkitUserDrag: string;
15+
MozUserSelect: string; // For some reason the Firefox property is in PascalCase.
16+
}
17+
18+
/**
19+
* Shallow-extends a stylesheet object with another stylesheet object.
20+
* @docs-private
21+
*/
22+
export function extendStyles(dest: CSSStyleDeclaration, source: Partial<DragCSSStyleDeclaration>) {
23+
for (let key in source) {
24+
if (source.hasOwnProperty(key)) {
25+
dest[key!] = source[key];
26+
}
27+
}
28+
29+
return dest;
30+
}
31+
32+
33+
/**
34+
* Toggles whether the native drag interactions should be enabled for an element.
35+
* @param element Element on which to toggle the drag interactions.
36+
* @param enable Whether the drag interactions should be enabled.
37+
* @docs-private
38+
*/
39+
export function toggleNativeDragInteractions(element: HTMLElement, enable: boolean) {
40+
const userSelect = enable ? '' : 'none';
41+
42+
extendStyles(element.style, {
43+
touchAction: enable ? '' : 'none',
44+
webkitUserDrag: enable ? '' : 'none',
45+
webkitTapHighlightColor: enable ? '' : 'transparent',
46+
userSelect: userSelect,
47+
msUserSelect: userSelect,
48+
webkitUserSelect: userSelect,
49+
MozUserSelect: userSelect
50+
});
51+
}

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

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,17 +1726,18 @@ class StandaloneDraggableWithMultipleHandles {
17261726

17271727
@Component({
17281728
template: `
1729-
<cdk-drop
1730-
style="display: block; width: 100px; background: pink;"
1729+
<div
1730+
cdkDrop
1731+
style="width: 100px; background: pink;"
17311732
[id]="dropZoneId"
1732-
[data]="items"
1733-
(dropped)="droppedSpy($event)">
1733+
[cdkDropData]="items"
1734+
(cdkDropDropped)="droppedSpy($event)">
17341735
<div
17351736
*ngFor="let item of items"
17361737
cdkDrag
17371738
[cdkDragData]="item"
17381739
style="width: 100%; height: ${ITEM_HEIGHT}px; background: red;">{{item}}</div>
1739-
</cdk-drop>
1740+
</div>
17401741
`
17411742
})
17421743
class DraggableInDropZone {
@@ -1770,12 +1771,13 @@ class DraggableInDropZone {
17701771
}
17711772
`],
17721773
template: `
1773-
<cdk-drop
1774-
orientation="horizontal"
1775-
[data]="items"
1776-
(dropped)="droppedSpy($event)">
1774+
<div
1775+
cdkDrop
1776+
cdkDropOrientation="horizontal"
1777+
[cdkDropData]="items"
1778+
(cdkDropDropped)="droppedSpy($event)">
17771779
<div *ngFor="let item of items" cdkDrag>{{item}}</div>
1778-
</cdk-drop>
1780+
</div>
17791781
`
17801782
})
17811783
class DraggableInHorizontalDropZone {
@@ -1789,13 +1791,13 @@ class DraggableInHorizontalDropZone {
17891791

17901792
@Component({
17911793
template: `
1792-
<cdk-drop style="display: block; width: 100px; background: pink;">
1794+
<div cdkDrop style="width: 100px; background: pink;">
17931795
<div *ngFor="let item of items" cdkDrag
17941796
style="width: 100%; height: ${ITEM_HEIGHT}px; background: red;">
17951797
{{item}}
17961798
<div class="custom-preview" *cdkDragPreview>Custom preview</div>
17971799
</div>
1798-
</cdk-drop>
1800+
</div>
17991801
`
18001802
})
18011803
class DraggableInDropZoneWithCustomPreview {
@@ -1807,13 +1809,13 @@ class DraggableInDropZoneWithCustomPreview {
18071809

18081810
@Component({
18091811
template: `
1810-
<cdk-drop style="display: block; width: 100px; background: pink;">
1812+
<div cdkDrop style="width: 100px; background: pink;">
18111813
<div *ngFor="let item of items" cdkDrag
18121814
style="width: 100%; height: ${ITEM_HEIGHT}px; background: red;">
18131815
{{item}}
18141816
<div class="custom-placeholder" *cdkDragPlaceholder>Custom placeholder</div>
18151817
</div>
1816-
</cdk-drop>
1818+
</div>
18171819
`
18181820
})
18191821
class DraggableInDropZoneWithCustomPlaceholder {
@@ -1839,21 +1841,23 @@ class DraggableInDropZoneWithCustomPlaceholder {
18391841
}
18401842
`],
18411843
template: `
1842-
<cdk-drop
1843-
#todoZone
1844-
[data]="todo"
1845-
[connectedTo]="[doneZone]"
1846-
(dropped)="droppedSpy($event)">
1844+
<div
1845+
cdkDrop
1846+
#todoZone="cdkDrop"
1847+
[cdkDropData]="todo"
1848+
[cdkDropConnectedTo]="[doneZone]"
1849+
(cdkDropDropped)="droppedSpy($event)">
18471850
<div [cdkDragData]="item" *ngFor="let item of todo" cdkDrag>{{item}}</div>
1848-
</cdk-drop>
1851+
</div>
18491852
1850-
<cdk-drop
1851-
#doneZone
1852-
[data]="done"
1853-
[connectedTo]="[todoZone]"
1854-
(dropped)="droppedSpy($event)">
1853+
<div
1854+
cdkDrop
1855+
#doneZone="cdkDrop"
1856+
[cdkDropData]="done"
1857+
[cdkDropConnectedTo]="[todoZone]"
1858+
(cdkDropDropped)="droppedSpy($event)">
18551859
<div [cdkDragData]="item" *ngFor="let item of done" cdkDrag>{{item}}</div>
1856-
</cdk-drop>
1860+
</div>
18571861
`
18581862
})
18591863
class ConnectedDropZones implements AfterViewInit {
@@ -1912,13 +1916,21 @@ class DraggableWithAlternateRoot {
19121916
}
19131917
`],
19141918
template: `
1915-
<cdk-drop #todoZone [connectedTo]="[doneZone]" (dropped)="droppedSpy($event)">
1919+
<div
1920+
cdkDrop
1921+
#todoZone="cdkDrop"
1922+
[cdkDropConnectedTo]="[doneZone]"
1923+
(cdkDropDropped)="droppedSpy($event)">
19161924
<div cdkDrag>One</div>
1917-
</cdk-drop>
1925+
</div>
19181926
1919-
<cdk-drop #doneZone [connectedTo]="[todoZone]" (dropped)="droppedSpy($event)">
1927+
<div
1928+
cdkDrop
1929+
#doneZone="cdkDrop"
1930+
[cdkDropConnectedTo]="[todoZone]"
1931+
(cdkDropDropped)="droppedSpy($event)">
19201932
<div cdkDrag>Two</div>
1921-
</cdk-drop>
1933+
</div>
19221934
`
19231935
})
19241936
class ConnectedDropZonesWithSingleItems {

0 commit comments

Comments
 (0)