Skip to content

Commit 34e848f

Browse files
crisbetommalerba
authored andcommitted
feat(drag-drop): allow for custom class to be set on preview (#17304)
When we create a preview for a dragged element, we put it in the `body` in order to avoid issues with `z-index` and `overflow`, however this makes it harder to style the element. When the previews were implemented initially, the thinking was the users could customize it either by providing a custom preview, in which case they can set as many classes as they want, or by targeting something like `.some-element.cdk-drag-preview` if they're using the default one. The problem with the latter is that it doesn't allow for something like a theme to be propagated to the preview. These changes add an extra input that allows users to set a class on the preview element for easier customization. Fixes #17089.
1 parent f10b222 commit 34e848f

File tree

4 files changed

+61
-2
lines changed

4 files changed

+61
-2
lines changed

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,6 +1977,31 @@ describe('CdkDrag', () => {
19771977
.toBeFalsy('Expected preview to be removed from the DOM if the transition timed out');
19781978
}));
19791979

1980+
it('should be able to set a single class on a preview', fakeAsync(() => {
1981+
const fixture = createComponent(DraggableInDropZone);
1982+
fixture.componentInstance.previewClass = 'custom-class';
1983+
fixture.detectChanges();
1984+
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
1985+
1986+
startDraggingViaMouse(fixture, item);
1987+
1988+
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
1989+
expect(preview.classList).toContain('custom-class');
1990+
}));
1991+
1992+
it('should be able to set multiple classes on a preview', fakeAsync(() => {
1993+
const fixture = createComponent(DraggableInDropZone);
1994+
fixture.componentInstance.previewClass = ['custom-class-1', 'custom-class-2'];
1995+
fixture.detectChanges();
1996+
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
1997+
1998+
startDraggingViaMouse(fixture, item);
1999+
2000+
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
2001+
expect(preview.classList).toContain('custom-class-1');
2002+
expect(preview.classList).toContain('custom-class-2');
2003+
}));
2004+
19802005
it('should emit the released event as soon as the item is released', fakeAsync(() => {
19812006
const fixture = createComponent(DraggableInDropZone);
19822007
fixture.detectChanges();
@@ -2759,6 +2784,19 @@ describe('CdkDrag', () => {
27592784
expect(preview.style.transform).toBe('translate3d(100px, 50px, 0px)');
27602785
}));
27612786

2787+
it('should be able to set a class on a custom preview', fakeAsync(() => {
2788+
const fixture = createComponent(DraggableInDropZoneWithCustomPreview);
2789+
fixture.componentInstance.previewClass = 'custom-class';
2790+
fixture.detectChanges();
2791+
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
2792+
2793+
startDraggingViaMouse(fixture, item);
2794+
2795+
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
2796+
expect(preview.classList).toContain('custom-preview');
2797+
expect(preview.classList).toContain('custom-class');
2798+
}));
2799+
27622800
it('should not throw when custom preview only has text', fakeAsync(() => {
27632801
const fixture = createComponent(DraggableInDropZoneWithCustomTextOnlyPreview);
27642802
fixture.detectChanges();
@@ -4467,6 +4505,7 @@ const DROP_ZONE_FIXTURE_TEMPLATE = `
44674505
cdkDrag
44684506
[cdkDragData]="item"
44694507
[cdkDragBoundary]="boundarySelector"
4508+
[cdkDragPreviewClass]="previewClass"
44704509
[style.height.px]="item.height"
44714510
[style.margin-bottom.px]="item.margin"
44724511
style="width: 100%; background: red;">{{item.value}}</div>
@@ -4485,6 +4524,7 @@ class DraggableInDropZone {
44854524
];
44864525
dropZoneId = 'items';
44874526
boundarySelector: string;
4527+
previewClass: string | string[];
44884528
sortedSpy = jasmine.createSpy('sorted spy');
44894529
droppedSpy = jasmine.createSpy('dropped spy').and.callFake((event: CdkDragDrop<string[]>) => {
44904530
moveItemInArray(this.items, event.previousIndex, event.currentIndex);
@@ -4604,6 +4644,7 @@ class DraggableInScrollableHorizontalDropZone extends DraggableInHorizontalDropZ
46044644
cdkDrag
46054645
[cdkDragConstrainPosition]="constrainPosition"
46064646
[cdkDragBoundary]="boundarySelector"
4647+
[cdkDragPreviewClass]="previewClass"
46074648
style="width: 100%; height: ${ITEM_HEIGHT}px; background: red;">
46084649
{{item}}
46094650
@@ -4623,6 +4664,7 @@ class DraggableInDropZoneWithCustomPreview {
46234664
items = ['Zero', 'One', 'Two', 'Three'];
46244665
boundarySelector: string;
46254666
renderCustomPreview = true;
4667+
previewClass: string | string[];
46264668
constrainPosition: (point: Point) => Point;
46274669
}
46284670

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
145145
*/
146146
@Input('cdkDragConstrainPosition') constrainPosition?: (point: Point, dragRef: DragRef) => Point;
147147

148+
/** Class to be added to the preview element. */
149+
@Input('cdkDragPreviewClass') previewClass: string | string[];
150+
148151
/** Emits when the user starts dragging the item. */
149152
@Output('cdkDragStarted') started: EventEmitter<CdkDragStart> = new EventEmitter<CdkDragStart>();
150153

@@ -343,6 +346,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
343346
ref.dragStartDelay = (typeof dragStartDelay === 'object' && dragStartDelay) ?
344347
dragStartDelay : coerceNumberProperty(dragStartDelay);
345348
ref.constrainPosition = this.constrainPosition;
349+
ref.previewClass = this.previewClass;
346350
ref
347351
.withBoundaryElement(this._getBoundaryElement())
348352
.withPlaceholderTemplate(placeholder)

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ export class DragRef<T = any> {
209209
*/
210210
dragStartDelay: number | {touch: number, mouse: number} = 0;
211211

212+
/** Class to be added to the preview element. */
213+
previewClass: string|string[]|undefined;
214+
212215
/** Whether starting to drag this element is disabled. */
213216
get disabled(): boolean {
214217
return this._disabled || !!(this._dropContainer && this._dropContainer.disabled);
@@ -844,6 +847,7 @@ export class DragRef<T = any> {
844847
*/
845848
private _createPreviewElement(): HTMLElement {
846849
const previewConfig = this._previewTemplate;
850+
const previewClass = this.previewClass;
847851
const previewTemplate = previewConfig ? previewConfig.template : null;
848852
let preview: HTMLElement;
849853

@@ -868,7 +872,7 @@ export class DragRef<T = any> {
868872
// It's important that we disable the pointer events on the preview, because
869873
// it can throw off the `document.elementFromPoint` calls in the `CdkDropList`.
870874
pointerEvents: 'none',
871-
// We have to reset the margin, because can throw off positioning relative to the viewport.
875+
// We have to reset the margin, because it can throw off positioning relative to the viewport.
872876
margin: '0',
873877
position: 'fixed',
874878
top: '0',
@@ -877,10 +881,17 @@ export class DragRef<T = any> {
877881
});
878882

879883
toggleNativeDragInteractions(preview, false);
880-
881884
preview.classList.add('cdk-drag-preview');
882885
preview.setAttribute('dir', this._direction);
883886

887+
if (previewClass) {
888+
if (Array.isArray(previewClass)) {
889+
previewClass.forEach(className => preview.classList.add(className));
890+
} else {
891+
preview.classList.add(previewClass);
892+
}
893+
}
894+
884895
return preview;
885896
}
886897

tools/public_api_guard/cdk/drag-drop.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export declare class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDes
2929
};
3030
lockAxis: 'x' | 'y';
3131
moved: Observable<CdkDragMove<T>>;
32+
previewClass: string | string[];
3233
released: EventEmitter<CdkDragRelease>;
3334
rootElementSelector: string;
3435
started: EventEmitter<CdkDragStart>;
@@ -237,6 +238,7 @@ export declare class DragRef<T = any> {
237238
y: -1 | 0 | 1;
238239
};
239240
}>;
241+
previewClass: string | string[] | undefined;
240242
released: Subject<{
241243
source: DragRef<any>;
242244
}>;

0 commit comments

Comments
 (0)