From 475dc21147208f6317ef4da70f8b3d603f33a97f Mon Sep 17 00:00:00 2001 From: crisbeto Date: Fri, 5 Oct 2018 13:10:17 +0300 Subject: [PATCH] feat(drag-drop): convert cdk-drop into a directive Converts `cdk-drop` into a directive and moves the handful of structural styles into JS. Fixes #13341. --- src/cdk/drag-drop/BUILD.bazel | 6 -- src/cdk/drag-drop/drag-drop-registry.spec.ts | 16 +---- src/cdk/drag-drop/drag-drop-registry.ts | 5 +- src/cdk/drag-drop/drag-drop.md | 46 ++++++------ src/cdk/drag-drop/drag-handle.ts | 5 +- src/cdk/drag-drop/drag-styling.ts | 51 +++++++++++++ src/cdk/drag-drop/drag.spec.ts | 72 +++++++++++-------- src/cdk/drag-drop/drag.ts | 9 +++ src/cdk/drag-drop/drop.scss | 24 ------- src/cdk/drag-drop/drop.ts | 33 ++++----- .../__name@dasherize__.component.html | 10 +-- src/demo-app/drag-drop/drag-drop-demo.html | 43 +++++------ ...k-drag-drop-connected-sorting-example.html | 20 ++++-- .../cdk-drag-drop-custom-preview-example.html | 4 +- ...-drag-drop-horizontal-sorting-example.html | 4 +- .../cdk-drag-drop-sorting-example.html | 4 +- 16 files changed, 201 insertions(+), 151 deletions(-) create mode 100644 src/cdk/drag-drop/drag-styling.ts delete mode 100644 src/cdk/drag-drop/drop.scss diff --git a/src/cdk/drag-drop/BUILD.bazel b/src/cdk/drag-drop/BUILD.bazel index 13028be1ffe6..1df5fffa8dd4 100644 --- a/src/cdk/drag-drop/BUILD.bazel +++ b/src/cdk/drag-drop/BUILD.bazel @@ -8,7 +8,6 @@ ng_module( name = "drag-drop", srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), module_name = "@angular/cdk/drag-drop", - assets = [":drop.css"], deps = [ "@rxjs", "//src/cdk/platform", @@ -32,11 +31,6 @@ ts_library( tsconfig = "//src/cdk:tsconfig-build.json", ) -sass_binary( - name = "drop_scss", - src = "drop.scss", -) - ts_web_test( name = "unit_tests", bootstrap = [ diff --git a/src/cdk/drag-drop/drag-drop-registry.spec.ts b/src/cdk/drag-drop/drag-drop-registry.spec.ts index 216617e4804e..29b2b8d2f74c 100644 --- a/src/cdk/drag-drop/drag-drop-registry.spec.ts +++ b/src/cdk/drag-drop/drag-drop-registry.spec.ts @@ -155,25 +155,15 @@ describe('DragDropRegistry', () => { expect(dispatchTouchEvent(document, 'touchmove').defaultPrevented).toBe(true); }); - it('should disable the native interactions on the body while dragging', () => { - const firstItem = testComponent.dragItems.first; - - registry.startDragging(firstItem, createMouseEvent('mousedown')); - expect(document.body.classList).toContain('cdk-drag-drop-disable-native-interactions'); - - registry.stopDragging(firstItem); - expect(document.body.classList).not.toContain('cdk-drag-drop-disable-native-interactions'); - }); - }); @Component({ template: ` - +
{{item}}
- +
- +
` }) class SimpleDropZone { diff --git a/src/cdk/drag-drop/drag-drop-registry.ts b/src/cdk/drag-drop/drag-drop-registry.ts index 13e798971dd0..7399fe3c5241 100644 --- a/src/cdk/drag-drop/drag-drop-registry.ts +++ b/src/cdk/drag-drop/drag-drop-registry.ts @@ -10,6 +10,7 @@ import {Injectable, NgZone, OnDestroy, Inject} from '@angular/core'; import {DOCUMENT} from '@angular/common'; import {supportsPassiveEventListeners} from '@angular/cdk/platform'; import {Subject} from 'rxjs'; +import {toggleNativeDragInteractions} from './drag-styling'; /** Event options that can be used to bind an active event. */ const activeEventOptions = supportsPassiveEventListeners() ? {passive: false} : false; @@ -118,7 +119,7 @@ export class DragDropRegistry implements OnDestroy { // We need to disable the native interactions on the entire body, because // the user can start marking text if they drag too far in Safari. - this._document.body.classList.add('cdk-drag-drop-disable-native-interactions'); + toggleNativeDragInteractions(this._document.body, false); // We explicitly bind __active__ listeners here, because newer browsers will default to // passive ones for `mousemove` and `touchmove`. The events need to be active, because we @@ -140,7 +141,7 @@ export class DragDropRegistry implements OnDestroy { if (this._activeDragInstances.size === 0) { this._clearGlobalListeners(); - this._document.body.classList.remove('cdk-drag-drop-disable-native-interactions'); + toggleNativeDragInteractions(this._document.body, true); } } diff --git a/src/cdk/drag-drop/drag-drop.md b/src/cdk/drag-drop/drag-drop.md index 28b3a19d3c5b..b251d8af4958 100644 --- a/src/cdk/drag-drop/drag-drop.md +++ b/src/cdk/drag-drop/drag-drop.md @@ -6,62 +6,62 @@ in addition to horizontal lists and locking along an axis. ### Getting started Start by importing `DragDropModule` into the `NgModule` where you want to use drag-and-drop features. You can now add the `cdkDrag` directive to elements to make them draggable. When -outside of a `` element, draggable elements can be freely moved around the page. -You can add `` elements to constrain where elements may be dropped. +outside of a `cdkDrop` element, draggable elements can be freely moved around the page. +You can add `cdkDrop` elements to constrain where elements may be dropped. ### Reordering lists -Adding `` around a set of `cdkDrag` elements groups the draggables into a +Adding `cdkDrop` around a set of `cdkDrag` elements groups the draggables into a reorderable collection. Items will automatically rearrange as an element moves. Note -that this will *not* update your data model; you can listen to the `dropped` event to +that this will *not* update your data model; you can listen to the `cdkDropDropped` event to update the data model once the user finishes dragging. ### Transferring items between lists -The `` component supports transferring dragged items between connected drop zones. -You can connect one or more `` instances together by setting the `connectedTo` +The `cdkDrop` directive supports transferring dragged items between connected drop zones. +You can connect one or more `cdkDrop` instances together by setting the `cdkDropConnectedTo` property. -Note that `connectedTo` works both with a direct reference to another ``, or by +Note that `cdkDropConnectedTo` works both with a direct reference to another `cdkDrop`, or by referencing the `id` of another drop container: ```html - - +
+
- - +
+
``` ### Attaching data -You can associate some arbitrary data with both `cdkDrag` and `` by setting -`cdkDragData` or `data`, respectively. Events fired from both directives include this data, -allowing you to easily identify the origin of the drag or drop interaction. +You can associate some arbitrary data with both `cdkDrag` and `cdkDrop` by setting `cdkDragData` +or `cdkDropData`, respectively. Events fired from both directives include this data, allowing +you to easily identify the origin of the drag or drop interaction. ```html - +
- +
``` ### Styling -The `cdkDrag` and `` directive include only those styles strictly necessary for +The `cdkDrag` and `cdkDrop` directive include only those styles strictly necessary for functionality. The application can then customize the elements by styling CSS classes added by the directives: | Selector | Description | |---------------------|--------------------------------------------------------------------------| -| `.cdk-drop` | Corresponds to the `` container. | +| `.cdk-drop` | Corresponds to the `cdkDrop` container. | | `.cdk-drag` | Corresponds to a `cdkDrag` instance. | | `.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. | -| `.cdk-drag-placeholder` | This is element that will be shown instead of the real element as it's being dragged inside a ``. By default this will look exactly like the element that is being sorted. | -| `.cdk-drop-dragging` | A class that is added to `` while the user is dragging an item. | +| `.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. | +| `.cdk-drop-dragging` | A class that is added to `cdkDrop` while the user is dragging an item. | ### Animations 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: through a list. * `.cdk-drag-animating` - This class is added to a `cdkDrag` when the user has stopped dragging. If you add a `transition` to it, the CDK will animate the element from its drop position to - the final position inside the `` container. + the final position inside the `cdkDrop` container. Example animations: @@ -105,14 +105,14 @@ This preview can be customized, though, by providing a custom template via `*cdk ### List orientation -The `cdk-drop` component assumes that lists are vertical by default. This can be +The `cdkDrop` directive assumes that lists are vertical by default. This can be changed by setting the `orientation` property to `"horizontal". ### Restricting movement along an axis By default, `cdkDrag` allows free movement in all directions. To restrict dragging to a -specific axis, you can set `cdkDragLockAxis` on `cdkDrag` or `lockAxis` on `` +specific axis, you can set `cdkDragLockAxis` on `cdkDrag` or `lockAxis` on `cdkDrop` to either `"x"` or `"y"`. diff --git a/src/cdk/drag-drop/drag-handle.ts b/src/cdk/drag-drop/drag-handle.ts index 6fd5282aaf66..a1fac346301c 100644 --- a/src/cdk/drag-drop/drag-handle.ts +++ b/src/cdk/drag-drop/drag-handle.ts @@ -7,6 +7,7 @@ */ import {Directive, ElementRef} from '@angular/core'; +import {toggleNativeDragInteractions} from './drag-styling'; /** Handle that can be used to drag and CdkDrag instance. */ @Directive({ @@ -16,5 +17,7 @@ import {Directive, ElementRef} from '@angular/core'; } }) export class CdkDragHandle { - constructor(public element: ElementRef) {} + constructor(public element: ElementRef) { + toggleNativeDragInteractions(element.nativeElement, false); + } } diff --git a/src/cdk/drag-drop/drag-styling.ts b/src/cdk/drag-drop/drag-styling.ts new file mode 100644 index 000000000000..1965e3342ce0 --- /dev/null +++ b/src/cdk/drag-drop/drag-styling.ts @@ -0,0 +1,51 @@ +/** + * @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 + */ + +/** + * Extended CSSStyleDeclaration that includes a couple of drag-related + * properties that aren't in the built-in TS typings. + */ +interface DragCSSStyleDeclaration extends CSSStyleDeclaration { + webkitUserDrag: string; + MozUserSelect: string; // For some reason the Firefox property is in PascalCase. +} + +/** + * Shallow-extends a stylesheet object with another stylesheet object. + * @docs-private + */ +export function extendStyles(dest: CSSStyleDeclaration, source: Partial) { + for (let key in source) { + if (source.hasOwnProperty(key)) { + dest[key!] = source[key]; + } + } + + return dest; +} + + +/** + * Toggles whether the native drag interactions should be enabled for an element. + * @param element Element on which to toggle the drag interactions. + * @param enable Whether the drag interactions should be enabled. + * @docs-private + */ +export function toggleNativeDragInteractions(element: HTMLElement, enable: boolean) { + const userSelect = enable ? '' : 'none'; + + extendStyles(element.style, { + touchAction: enable ? '' : 'none', + webkitUserDrag: enable ? '' : 'none', + webkitTapHighlightColor: enable ? '' : 'transparent', + userSelect: userSelect, + msUserSelect: userSelect, + webkitUserSelect: userSelect, + MozUserSelect: userSelect + }); +} diff --git a/src/cdk/drag-drop/drag.spec.ts b/src/cdk/drag-drop/drag.spec.ts index 0848461d2d1f..2b3ba46a4f51 100644 --- a/src/cdk/drag-drop/drag.spec.ts +++ b/src/cdk/drag-drop/drag.spec.ts @@ -1726,17 +1726,18 @@ class StandaloneDraggableWithMultipleHandles { @Component({ template: ` - + [cdkDropData]="items" + (cdkDropDropped)="droppedSpy($event)">
{{item}}
-
+ ` }) class DraggableInDropZone { @@ -1770,12 +1771,13 @@ class DraggableInDropZone { } `], template: ` - +
{{item}}
- +
` }) class DraggableInHorizontalDropZone { @@ -1789,13 +1791,13 @@ class DraggableInHorizontalDropZone { @Component({ template: ` - +
{{item}}
Custom preview
- +
` }) class DraggableInDropZoneWithCustomPreview { @@ -1807,13 +1809,13 @@ class DraggableInDropZoneWithCustomPreview { @Component({ template: ` - +
{{item}}
Custom placeholder
- +
` }) class DraggableInDropZoneWithCustomPlaceholder { @@ -1839,21 +1841,23 @@ class DraggableInDropZoneWithCustomPlaceholder { } `], template: ` - +
{{item}}
- +
- +
{{item}}
- +
` }) class ConnectedDropZones implements AfterViewInit { @@ -1912,13 +1916,21 @@ class DraggableWithAlternateRoot { } `], template: ` - +
One
- +
- +
Two
- +
` }) class ConnectedDropZonesWithSingleItems { diff --git a/src/cdk/drag-drop/drag.ts b/src/cdk/drag-drop/drag.ts index 571bbbd152eb..99cabb583086 100644 --- a/src/cdk/drag-drop/drag.ts +++ b/src/cdk/drag-drop/drag.ts @@ -44,6 +44,7 @@ import {CdkDragPlaceholder} from './drag-placeholder'; import {CdkDragPreview} from './drag-preview'; import {CDK_DROP_CONTAINER, CdkDropContainer} from './drop-container'; import {getTransformTransitionDurationInMs} from './transition-duration'; +import {extendStyles, toggleNativeDragInteractions} from './drag-styling'; // TODO(crisbeto): add auto-scrolling functionality. @@ -254,6 +255,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy { const rootElement = this._rootElement = this._getRootElement(); rootElement.addEventListener('mousedown', this._pointerDown); rootElement.addEventListener('touchstart', this._pointerDown); + toggleNativeDragInteractions(rootElement , false); }); } @@ -525,6 +527,13 @@ export class CdkDrag implements AfterViewInit, OnDestroy { this._setTransform(preview, elementRect.left, elementRect.top); } + extendStyles(preview.style, { + position: 'fixed', + top: '0', + left: '0', + zIndex: '1000' + }); + preview.classList.add('cdk-drag-preview'); preview.setAttribute('dir', this._dir ? this._dir.value : 'ltr'); diff --git a/src/cdk/drag-drop/drop.scss b/src/cdk/drag-drop/drop.scss deleted file mode 100644 index 31bd978c2873..000000000000 --- a/src/cdk/drag-drop/drop.scss +++ /dev/null @@ -1,24 +0,0 @@ -$cdk-z-index-drag-preview: 1000; - -.cdk-drag-preview { - position: fixed; - top: 0; - left: 0; - z-index: $cdk-z-index-drag-preview; -} - -.cdk-drag, -.cdk-drag-handle, -.cdk-drag-drop-disable-native-interactions { - touch-action: none; - -webkit-user-drag: none; - -webkit-tap-highlight-color: transparent; - - // stylelint-disable material/no-prefixes - // normally we have a mixin for these, but it's in material/core. - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - // stylelint-enable material/no-prefixes -} diff --git a/src/cdk/drag-drop/drop.ts b/src/cdk/drag-drop/drop.ts index 722c9bbc3413..2970751cbee7 100644 --- a/src/cdk/drag-drop/drop.ts +++ b/src/cdk/drag-drop/drop.ts @@ -8,8 +8,6 @@ import {coerceArray} from '@angular/cdk/coercion'; import { - ChangeDetectionStrategy, - Component, ContentChildren, ElementRef, EventEmitter, @@ -19,8 +17,8 @@ import { OnInit, Output, QueryList, - ViewEncapsulation, Optional, + Directive, } from '@angular/core'; import {Directionality} from '@angular/cdk/bidi'; import {CdkDrag} from './drag'; @@ -40,14 +38,9 @@ let _uniqueIdCounter = 0; const DROP_PROXIMITY_THRESHOLD = 0.05; /** Container that wraps a set of draggable items. */ -@Component({ - moduleId: module.id, - selector: 'cdk-drop', +@Directive({ + selector: '[cdkDrop], cdk-drop', exportAs: 'cdkDrop', - template: '', - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - styleUrls: ['drop.css'], providers: [ {provide: CDK_DROP_CONTAINER, useExisting: CdkDrop}, ], @@ -66,13 +59,13 @@ export class CdkDrop implements OnInit, OnDestroy { * container's items can be transferred. Can either be references to other drop containers, * or their unique IDs. */ - @Input() connectedTo: (CdkDrop | string)[] | CdkDrop | string = []; + @Input('cdkDropConnectedTo') connectedTo: (CdkDrop | string)[] | CdkDrop | string = []; /** Arbitrary data to attach to this container. */ - @Input() data: T; + @Input('cdkDropData') data: T; /** Direction in which the list is oriented. */ - @Input() orientation: 'horizontal' | 'vertical' = 'vertical'; + @Input('cdkDropOrientation') orientation: 'horizontal' | 'vertical' = 'vertical'; /** * Unique ID for the drop zone. Can be used as a reference @@ -81,27 +74,31 @@ export class CdkDrop implements OnInit, OnDestroy { @Input() id: string = `cdk-drop-${_uniqueIdCounter++}`; /** Locks the position of the draggable elements inside the container along the specified axis. */ - @Input() lockAxis: 'x' | 'y'; + @Input('cdkDropLockAxis') lockAxis: 'x' | 'y'; /** * Function that is used to determine whether an item * is allowed to be moved into a drop container. */ - @Input() enterPredicate: (drag?: CdkDrag, drop?: CdkDrop) => boolean = () => true; + @Input('cdkDropEnterPredication') + enterPredicate: (drag?: CdkDrag, drop?: CdkDrop) => boolean = () => true /** Emits when the user drops an item inside the container. */ - @Output() dropped: EventEmitter> = new EventEmitter>(); + @Output('cdkDropDropped') + dropped: EventEmitter> = new EventEmitter>(); /** * Emits when the user has moved a new drag item into this container. */ - @Output() entered: EventEmitter> = new EventEmitter>(); + @Output('cdkDropEntered') + entered: EventEmitter> = new EventEmitter>(); /** * Emits when the user removes an item from the container * by dragging it into another container. */ - @Output() exited: EventEmitter> = new EventEmitter>(); + @Output('cdkDropExited') + exited: EventEmitter> = new EventEmitter>(); constructor( public element: ElementRef, diff --git a/src/cdk/schematics/ng-generate/drag-drop/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html b/src/cdk/schematics/ng-generate/drag-drop/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html index b79b0c0d7bfe..6a4f29143640 100644 --- a/src/cdk/schematics/ng-generate/drag-drop/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html +++ b/src/cdk/schematics/ng-generate/drag-drop/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html @@ -1,15 +1,17 @@

To do

- +
{{item}}
- +

Done

- +
{{item}}
- +
diff --git a/src/demo-app/drag-drop/drag-drop-demo.html b/src/demo-app/drag-drop/drag-drop-demo.html index a82df8bd50be..ecfe44a92989 100644 --- a/src/demo-app/drag-drop/drag-drop-demo.html +++ b/src/demo-app/drag-drop/drag-drop-demo.html @@ -1,48 +1,51 @@

To do

- +
{{item}}
- +

Done

- +
{{item}}
- +

Horizontal list

- +
{{item}}
- +
diff --git a/src/material-examples/cdk-drag-drop-connected-sorting/cdk-drag-drop-connected-sorting-example.html b/src/material-examples/cdk-drag-drop-connected-sorting/cdk-drag-drop-connected-sorting-example.html index 5bd205317897..7d0d3bd8ec12 100644 --- a/src/material-examples/cdk-drag-drop-connected-sorting/cdk-drag-drop-connected-sorting-example.html +++ b/src/material-examples/cdk-drag-drop-connected-sorting/cdk-drag-drop-connected-sorting-example.html @@ -1,16 +1,28 @@

To do

- +
{{item}}
- +

Done

- +
{{item}}
- +
diff --git a/src/material-examples/cdk-drag-drop-custom-preview/cdk-drag-drop-custom-preview-example.html b/src/material-examples/cdk-drag-drop-custom-preview/cdk-drag-drop-custom-preview-example.html index 8e469cad9ac4..8b40f69d0671 100644 --- a/src/material-examples/cdk-drag-drop-custom-preview/cdk-drag-drop-custom-preview-example.html +++ b/src/material-examples/cdk-drag-drop-custom-preview/cdk-drag-drop-custom-preview-example.html @@ -1,6 +1,6 @@ - +
{{movie.title}}
- +
diff --git a/src/material-examples/cdk-drag-drop-horizontal-sorting/cdk-drag-drop-horizontal-sorting-example.html b/src/material-examples/cdk-drag-drop-horizontal-sorting/cdk-drag-drop-horizontal-sorting-example.html index 78bd370f7000..1c0aa1d37b83 100644 --- a/src/material-examples/cdk-drag-drop-horizontal-sorting/cdk-drag-drop-horizontal-sorting-example.html +++ b/src/material-examples/cdk-drag-drop-horizontal-sorting/cdk-drag-drop-horizontal-sorting-example.html @@ -1,3 +1,3 @@ - +
{{timePeriod}}
- +
diff --git a/src/material-examples/cdk-drag-drop-sorting/cdk-drag-drop-sorting-example.html b/src/material-examples/cdk-drag-drop-sorting/cdk-drag-drop-sorting-example.html index a4cb04aaabfe..5f7e231fbc40 100644 --- a/src/material-examples/cdk-drag-drop-sorting/cdk-drag-drop-sorting-example.html +++ b/src/material-examples/cdk-drag-drop-sorting/cdk-drag-drop-sorting-example.html @@ -1,3 +1,3 @@ - +
{{movie}}
- +