Skip to content

Commit ce33294

Browse files
feat(module:modal): draggable (#8419)
1 parent ec7ec35 commit ce33294

9 files changed

Lines changed: 116 additions & 15 deletions

File tree

components/modal/demo/draggable.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
order: 11
3+
title:
4+
zh-CN: 基本
5+
en-US: Draggable
6+
---
7+
8+
## zh-CN
9+
10+
可拖动模态。
11+
12+
## en-US
13+
14+
Draggable modal.

components/modal/demo/draggable.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'nz-demo-modal-draggable',
5+
template: `
6+
<button nz-button nzType="default" (click)="showModal()">
7+
<span>Open Draggable Modal</span>
8+
</button>
9+
10+
<nz-modal
11+
nzDraggable
12+
nzCentered
13+
[(nzVisible)]="isVisible"
14+
nzTitle="Draggable Modal"
15+
(nzOnCancel)="handleCancel()"
16+
(nzOnOk)="handleOk()"
17+
>
18+
<ng-container *nzModalContent>
19+
<p>Just don't learn physics at school and your life will be full of magic and miracles.</p>
20+
<p>Day before yesterday I saw a rabbit, and yesterday a deer, and today, you.</p>
21+
</ng-container>
22+
</nz-modal>
23+
`
24+
})
25+
export class NzDemoModalDraggableComponent {
26+
isVisible = false;
27+
28+
constructor() {}
29+
30+
showModal(): void {
31+
this.isVisible = true;
32+
}
33+
34+
handleOk(): void {
35+
console.log('Button ok clicked!');
36+
this.isVisible = false;
37+
}
38+
39+
handleCancel(): void {
40+
console.log('Button cancel clicked!');
41+
this.isVisible = false;
42+
}
43+
}

components/modal/doc/index.en-US.md

Lines changed: 10 additions & 12 deletions
Large diffs are not rendered by default.

components/modal/doc/index.zh-CN.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { NzModalModule } from 'ng-zorro-antd/modal';
4040
| nzCancelLoading | 取消按钮 loading | `boolean` | `false` |
4141
| nzOkDisabled | 是否禁用确定按钮 | `boolean` | `false` |
4242
| nzCancelDisabled | 是否禁用取消按钮 | `boolean` | `false` |
43+
| nzDraggable | 模态框是否可拖动 | `boolean` | `false` |
4344
| nzFooter | 底部内容。<i>1. 仅在普通模式下有效。<br>2. 可通过传入 ModalButtonOptions 来最大程度自定义按钮(详见案例或下方说明)。<br>3. 当不需要底部时,可以设为 null</i> | string<br>TemplateRef<br>ModalButtonOptions | 默认的确定取消按钮 |
4445
| nzKeyboard | 是否支持键盘 esc 关闭 | `boolean` | `true` |
4546
| nzMask | 是否展示遮罩 | `boolean` | `true` ||
@@ -119,7 +120,7 @@ constructor(modal: NzModalService) {
119120
| close(result: any) | 关闭(隐藏)对话框。<i>注:当用于以服务方式创建的对话框,此方法将直接 销毁 对话框(同 destroy 方法)</i> |
120121
| destroy(result: any) | 销毁对话框。<i>注:仅用于服务方式创建的对话框(非服务方式创建的对话框,此方法只会隐藏对话框)</i> |
121122
| getContentComponent() | 获取对话框内容中`nzContent`的 Component 实例 instance。<i>注:当对话框还未初始化完毕(`ngOnInit`未执行)时,此函数将返回`undefined`</i> |
122-
| getContentComponentRef() | 获取对话框内容中`nzContent`的 Component 引用 ComponentRef。<i>注:当对话框还未初始化完毕(`ngOnInit`未执行)时,此函数将返回`null`</i> |
123+
| getContentComponentRef() | 获取对话框内容中`nzContent`的 Component 引用 ComponentRef。<i>注:当对话框还未初始化完毕(`ngOnInit`未执行)时,此函数将返回`null`</i> |
123124
| triggerOk() | 手动触发 nzOnOk |
124125
| triggerCancel() | 手动触发 nzOnCancel |
125126
| updateConfig(config: ModalOptions): void | 更新配置 |

components/modal/modal-container.component.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { FocusTrapFactory } from '@angular/cdk/a11y';
7+
import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
78
import { OverlayRef } from '@angular/cdk/overlay';
89
import { CdkPortalOutlet, PortalModule } from '@angular/cdk/portal';
910
import { DOCUMENT, NgClass, NgStyle } from '@angular/common';
@@ -38,6 +39,9 @@ import { ModalOptions } from './modal-types';
3839
template: `
3940
<div
4041
#modalElement
42+
cdkDrag
43+
cdkDragBoundary=".cdk-overlay-container"
44+
[cdkDragDisabled]="!config.nzDraggable"
4145
role="document"
4246
class="ant-modal"
4347
[ngClass]="config.nzClassName!"
@@ -49,7 +53,7 @@ import { ModalOptions } from './modal-types';
4953
<button nz-modal-close (click)="onCloseClick()"></button>
5054
}
5155
@if (config.nzTitle) {
52-
<div nz-modal-title></div>
56+
<div nz-modal-title cdkDragHandle [style.cursor]="config.nzDraggable ? 'move' : 'auto'"></div>
5357
}
5458
5559
<div class="ant-modal-body" [ngStyle]="config.nzBodyStyle!">
@@ -92,7 +96,9 @@ import { ModalOptions } from './modal-types';
9296
NzModalTitleComponent,
9397
PortalModule,
9498
NzModalFooterComponent,
95-
NzPipesModule
99+
NzPipesModule,
100+
CdkDrag,
101+
CdkDragHandle
96102
],
97103
standalone: true
98104
})

components/modal/modal-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class ModalOptions<T = NzSafeAny, D = NzSafeAny, R = NzSafeAny> {
2828
nzOkDisabled?: boolean = false;
2929
nzCancelDisabled?: boolean = false;
3030
nzCancelLoading?: boolean = false;
31+
nzDraggable?: boolean = false;
3132
nzNoAnimation?: boolean = false;
3233
nzAutofocus?: 'ok' | 'cancel' | 'auto' | null = 'auto';
3334
nzMask?: boolean;

components/modal/modal.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class NzModalComponent<T extends ModalOptions = NzSafeAny, R = NzSafeAny>
5757
static ngAcceptInputType_nzNoAnimation: BooleanInput;
5858
static ngAcceptInputType_nzOkDanger: BooleanInput;
5959
static ngAcceptInputType_nzCentered: BooleanInput;
60+
static ngAcceptInputType_nzDraggable: BooleanInput;
6061

6162
@Input() @InputBoolean() nzMask?: boolean;
6263
@Input() @InputBoolean() nzMaskClosable?: boolean;
@@ -70,6 +71,7 @@ export class NzModalComponent<T extends ModalOptions = NzSafeAny, R = NzSafeAny>
7071
@Input() @InputBoolean() nzKeyboard: boolean = true;
7172
@Input() @InputBoolean() nzNoAnimation = false;
7273
@Input() @InputBoolean() nzCentered = false;
74+
@Input() @InputBoolean() nzDraggable = false;
7375
@Input() nzContent?: string | TemplateRef<{}> | Type<T>;
7476
@Input() nzFooter?: string | TemplateRef<{}> | Array<ModalButtonOptions<T>> | null;
7577
@Input() nzZIndex: number = 1000;

components/modal/modal.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,7 @@ describe('NzModal', () => {
13281328
fixture.detectChanges();
13291329
expect((overlayContainerElement.querySelector('.ant-modal') as HTMLDivElement).style.width).toBe('416px');
13301330
expect(modalRef.getConfig().nzMaskClosable).toBe(false);
1331+
expect(modalRef.getConfig().nzDraggable).toBe(false);
13311332
expect(modalRef.getConfig().nzCentered).toBe(false);
13321333
expect(overlayContainerElement.querySelectorAll('nz-modal-confirm-container').length).toBe(1);
13331334
expect(overlayContainerElement.querySelector('.ant-modal-confirm-title')!.textContent).toBe('Test Title');
@@ -1680,6 +1681,37 @@ describe('NzModal', () => {
16801681

16811682
expect(overlayContainerElement.querySelector('nz-modal-container')).toBeNull();
16821683
}));
1684+
1685+
it('should be draggable when nzDraggable is set to true', fakeAsync(() => {
1686+
componentInstance.isVisible = true;
1687+
componentInstance.isDraggable = true;
1688+
componentFixture.detectChanges();
1689+
flush();
1690+
expect(overlayContainerElement.querySelector('.cdk-drag')).not.toBeNull();
1691+
1692+
componentInstance.isDraggable = false;
1693+
componentFixture.detectChanges();
1694+
flush();
1695+
1696+
expect(overlayContainerElement.querySelector('.cdk-drag-disabled')).not.toBeNull();
1697+
1698+
componentFixture.destroy();
1699+
}));
1700+
1701+
it('should have "move" cursor on the top of modal when modal is draggable', fakeAsync(() => {
1702+
componentInstance.isVisible = true;
1703+
componentInstance.isDraggable = true;
1704+
componentFixture.detectChanges();
1705+
flush();
1706+
const modalHeader = overlayContainerElement.querySelector('.ant-modal-header');
1707+
expect(getComputedStyle(modalHeader!).cursor).toEqual('move');
1708+
1709+
componentInstance.isVisible = true;
1710+
componentInstance.isDraggable = false;
1711+
componentFixture.detectChanges();
1712+
flush();
1713+
expect(getComputedStyle(modalHeader!).cursor).toEqual('auto');
1714+
}));
16831715
});
16841716
});
16851717

@@ -1764,6 +1796,7 @@ class TestWithModalContentComponent {
17641796
<nz-modal
17651797
[(nzVisible)]="isVisible"
17661798
[nzContent]="content"
1799+
[nzDraggable]="isDraggable"
17671800
nzTitle="Test Title"
17681801
(nzOnCancel)="handleCancel()"
17691802
(nzOnOk)="handleOk()"
@@ -1775,6 +1808,7 @@ class TestWithModalContentComponent {
17751808
})
17761809
class TestModalComponent {
17771810
isVisible = false;
1811+
isDraggable = false;
17781812
cancelSpy = jasmine.createSpy('cancel spy');
17791813
okSpy = jasmine.createSpy('ok spy');
17801814
@ViewChild(NzModalComponent) nzModalComponent!: NzModalComponent;

components/modal/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export function getConfigFromComponent<T extends ModalOptions>(component: T): Mo
3333
nzCancelLoading,
3434
nzKeyboard,
3535
nzNoAnimation,
36+
nzDraggable,
3637
nzContent,
3738
nzFooter,
3839
nzZIndex,
@@ -61,6 +62,7 @@ export function getConfigFromComponent<T extends ModalOptions>(component: T): Mo
6162
nzCentered,
6263
nzMask,
6364
nzMaskClosable,
65+
nzDraggable,
6466
nzClosable,
6567
nzOkLoading,
6668
nzOkDisabled,

0 commit comments

Comments
 (0)