diff --git a/packages/theme/src/services/modal/demo/drag.md b/packages/theme/src/services/modal/demo/drag.md
new file mode 100644
index 000000000..3be705d5e
--- /dev/null
+++ b/packages/theme/src/services/modal/demo/drag.md
@@ -0,0 +1,37 @@
+---
+title:
+ zh-CN: 拖动
+ en-US: Drag
+order: 1
+---
+
+## zh-CN
+
+支持拖动对话框。
+
+## en-US
+
+Support for dragging dialogs.
+
+```ts
+import { Component } from '@angular/core';
+import { ModalHelper } from '@delon/theme';
+import { DemoModalComponent } from '@shared';
+import { NzMessageService } from 'ng-zorro-antd/message';
+
+@Component({
+ selector: 'app-demo',
+ template: `
+
+ `,
+})
+export class DemoComponent {
+ constructor(private modalHelper: ModalHelper, private msg: NzMessageService) {}
+
+ open(): void {
+ this.modalHelper.create(DemoModalComponent, { record: { a: 1, b: '2', c: new Date() } }, { drag: true }).subscribe(res => {
+ this.msg.info(res);
+ });
+ }
+}
+```
diff --git a/packages/theme/src/services/modal/index.en-US.md b/packages/theme/src/services/modal/index.en-US.md
index b27e6f7ae..23be6d41d 100644
--- a/packages/theme/src/services/modal/index.en-US.md
+++ b/packages/theme/src/services/modal/index.en-US.md
@@ -53,4 +53,5 @@ Your body content
| `size` | Specify modal size | `sm,md,lg,xl,number` | `lg` |
| `exact` | Exact match return value, default is `true`, If the return value is not null (`null` or `undefined`) is considered successful, otherwise it is considered error. | `boolean` | `true` |
| `includeTabs` | Whether to wrap the tab page | `boolean` | `false` |
+| `drag` | Drag | `boolean, ModalHelperDragOptions` | - |
| `modalOptions` | nz-modal raw parameters [ModalOptions](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/modal/modal-types.ts) | `ModalOptions` | - |
diff --git a/packages/theme/src/services/modal/index.zh-CN.md b/packages/theme/src/services/modal/index.zh-CN.md
index adb4ce7f4..ef5389dd8 100644
--- a/packages/theme/src/services/modal/index.zh-CN.md
+++ b/packages/theme/src/services/modal/index.zh-CN.md
@@ -53,4 +53,5 @@ Your body content
| `size` | 指定对话框大小 | `sm,md,lg,xl,number` | `lg` |
| `exact` | 是否精准(默认:`true`),若返回值非空值(`null`或`undefined`)视为成功,否则视为错误 | `boolean` | `true` |
| `includeTabs` | 是否包裹标签页 | `boolean` | `false` |
+| `drag` | 支持拖动 | `boolean, ModalHelperDragOptions` | - |
| `modalOptions` | 对话框 [ModalOptions](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/modal/modal-types.ts) 参数 | `ModalOptions` | - |
diff --git a/packages/theme/src/services/modal/modal.helper.ts b/packages/theme/src/services/modal/modal.helper.ts
index d4a11bcfb..58c5f5cb3 100644
--- a/packages/theme/src/services/modal/modal.helper.ts
+++ b/packages/theme/src/services/modal/modal.helper.ts
@@ -1,5 +1,7 @@
-import { Injectable, TemplateRef, Type } from '@angular/core';
-import { Observable, Observer } from 'rxjs';
+import { DragDrop, DragRef } from '@angular/cdk/drag-drop';
+import { DOCUMENT } from '@angular/common';
+import { Inject, Injectable, TemplateRef, Type } from '@angular/core';
+import { Observable, Observer, filter, take } from 'rxjs';
import { deepMerge } from '@delon/util/other';
import type { NzSafeAny } from 'ng-zorro-antd/core/types';
@@ -14,6 +16,17 @@ export interface ModalHelperOptions {
exact?: boolean;
/** 是否包裹标签页,修复模态包含标签间距问题 */
includeTabs?: boolean;
+ /**
+ * 是否支持拖动,默认是通过标题来触发
+ */
+ drag?: ModalHelperDragOptions | boolean;
+}
+
+export interface ModalHelperDragOptions {
+ /**
+ * 指定拖地区域的类名,若指定为 `null` 时表示整个对话框,默认:`.modal-header, .ant-modal-title`
+ */
+ handleCls?: string | null;
}
/**
@@ -21,7 +34,27 @@ export interface ModalHelperOptions {
*/
@Injectable({ providedIn: 'root' })
export class ModalHelper {
- constructor(private srv: NzModalService) {}
+ private document: Document;
+ private dragClsPrefix = 'MODAL-DRAG';
+
+ constructor(private srv: NzModalService, private drag: DragDrop, @Inject(DOCUMENT) doc: NzSafeAny) {
+ this.document = doc;
+ }
+
+ private createDragRef(options: ModalHelperDragOptions, wrapCls: string): DragRef {
+ const wrapEl = this.document.querySelector(wrapCls) as HTMLDivElement;
+ const modalEl = wrapEl.firstChild as HTMLDivElement;
+ const handelEl = options.handleCls ? wrapEl.querySelector(options.handleCls) : null;
+ if (handelEl) {
+ handelEl.classList.add(`${this.dragClsPrefix}-HANDLE`);
+ }
+
+ return this.drag
+ .createDrag(handelEl ?? modalEl)
+ .withHandles([handelEl ?? modalEl])
+ .withBoundaryElement(wrapEl)
+ .withRootElement(modalEl);
+ }
/**
* 构建一个对话框
@@ -53,7 +86,7 @@ export class ModalHelper {
options
);
return new Observable((observer: Observer) => {
- const { size, includeTabs, modalOptions } = options as ModalHelperOptions;
+ const { size, includeTabs, modalOptions, drag } = options as ModalHelperOptions;
let cls = '';
let width = '';
if (size) {
@@ -70,6 +103,16 @@ export class ModalHelper {
cls += ` ${modalOptions.nzWrapClassName}`;
delete modalOptions.nzWrapClassName;
}
+ let dragOptions: ModalHelperDragOptions | null;
+ let dragWrapCls = `${this.dragClsPrefix}-${+new Date()}`;
+ let dragRef: DragRef | null;
+ if (drag != null && drag !== false) {
+ dragOptions = {
+ handleCls: `.modal-header, .ant-modal-title`,
+ ...(typeof drag === 'object' ? drag : {})
+ };
+ cls += ` ${this.dragClsPrefix} ${dragWrapCls}`;
+ }
const defaultOptions: ModalOptions = {
nzWrapClassName: cls,
nzContent: comp,
@@ -78,7 +121,15 @@ export class ModalHelper {
nzComponentParams: params
};
const subject = this.srv.create({ ...defaultOptions, ...modalOptions });
- const afterClose$ = subject.afterClose.subscribe((res: NzSafeAny) => {
+ subject.afterOpen
+ .pipe(
+ take(1),
+ filter(() => dragOptions != null)
+ )
+ .subscribe(() => {
+ dragRef = this.createDragRef(dragOptions!!, `.${dragWrapCls}`);
+ });
+ subject.afterClose.pipe(take(1)).subscribe((res: NzSafeAny) => {
if (options!.exact === true) {
if (res != null) {
observer.next(res);
@@ -87,7 +138,7 @@ export class ModalHelper {
observer.next(res);
}
observer.complete();
- afterClose$.unsubscribe();
+ dragRef?.dispose();
});
});
}
diff --git a/packages/theme/src/services/modal/modal.spec.ts b/packages/theme/src/services/modal/modal.spec.ts
index 3440d0bbb..9575030ec 100644
--- a/packages/theme/src/services/modal/modal.spec.ts
+++ b/packages/theme/src/services/modal/modal.spec.ts
@@ -70,6 +70,31 @@ describe('theme: ModalHelper', () => {
fixture.detectChanges();
}));
});
+ describe('#drag', () => {
+ it('should be working', fakeAsync(() => {
+ modal
+ .create(TestModalComponent, { ret: 'true' }, { drag: true, modalOptions: { nzTitle: 'test' } })
+ .subscribe();
+ fixture.detectChanges();
+ tick(1000);
+ fixture.detectChanges();
+ expect(document.querySelectorAll('.MODAL-DRAG').length).toBe(1);
+ }));
+ it('#handleCls', fakeAsync(() => {
+ modal
+ .create(
+ TestModalComponent,
+ { ret: 'true' },
+ { drag: { handleCls: '.handle' }, modalOptions: { nzTitle: 'test' } }
+ )
+ .subscribe();
+ fixture.detectChanges();
+ tick(1000);
+ fixture.detectChanges();
+ const handle = document.querySelector('.MODAL-DRAG-HANDLE');
+ expect(handle?.classList).toContain('handle');
+ }));
+ });
});
describe('#createStatic', () => {
@@ -113,7 +138,7 @@ describe('theme: ModalHelper', () => {
});
@Component({
- template: ` modal{{ id }}
`
+ template: ` modal{{ id }}
`
})
class TestModalComponent {
id = '';
diff --git a/packages/theme/system/antd/_modal.less b/packages/theme/system/antd/_modal.less
index e9314a45a..65037b972 100644
--- a/packages/theme/system/antd/_modal.less
+++ b/packages/theme/system/antd/_modal.less
@@ -123,3 +123,7 @@
}
}
}
+
+.MODAL-DRAG-HANDLE {
+ cursor: move;
+}
diff --git a/scripts/site/route-paths.txt b/scripts/site/route-paths.txt
index 98ee91ca6..3cfb56b30 100644
--- a/scripts/site/route-paths.txt
+++ b/scripts/site/route-paths.txt
@@ -334,4 +334,4 @@
/util/pipes-format/en
/util/pipes-format/zh
/util/token/en
-/util/token/zh
+/util/token/zh
\ No newline at end of file