1
- import { DOCUMENT } from '@angular/common' ;
2
1
import {
3
2
AfterViewInit ,
4
3
Directive ,
5
4
ElementRef ,
6
5
EventEmitter ,
7
- Inject ,
8
6
Input ,
9
7
OnDestroy ,
8
+ Optional ,
10
9
Output ,
11
10
Renderer2 ,
12
11
} from '@angular/core' ;
13
- import { fromEvent , Subject , Subscription , takeUntil } from 'rxjs' ;
12
+ import { fromEvent , Subject , takeUntil } from 'rxjs' ;
13
+
14
+ @Directive ( {
15
+ selector : '[auiContainerForResizable]' ,
16
+ host : {
17
+ class : 'aui-container-for-resizable' ,
18
+ } ,
19
+ standalone : true ,
20
+ } )
21
+ export class ContainerForResizableDirective {
22
+ constructor ( readonly el : ElementRef ) { }
23
+ }
14
24
15
25
/**
16
26
* 使用此指令需要引入resizable.scss
17
- * 因为参考线是基于absolute定位的,所以需要在预期的地方加上relative(不自动加是为了避免修改业务布局而导致使用者出现意外的异常)
18
27
*/
19
28
@Directive ( {
20
29
selector : '[auiResizable]' ,
30
+ host : {
31
+ class : 'aui-resizable' ,
32
+ } ,
21
33
standalone : true ,
22
34
} )
23
35
export class ResizableDirective implements AfterViewInit , OnDestroy {
24
36
@Input ( )
25
- containerElement : Element ; // 要插入参考线的容器元素
37
+ minWidth : string | number ;
26
38
27
39
@Input ( )
28
- minWidth : string ;
29
-
30
- @Input ( )
31
- maxWidth : string ;
40
+ maxWidth : string | number ;
32
41
33
42
@Output ( )
34
- resizeStartEvent = new EventEmitter < number > ( ) ;
43
+ resizeStart = new EventEmitter < number > ( ) ;
35
44
36
45
@Output ( )
37
- resizingEvent = new EventEmitter < number > ( ) ;
46
+ resizing = new EventEmitter < number > ( ) ;
38
47
39
48
@Output ( )
40
- resizeEndEvent = new EventEmitter < number > ( ) ;
49
+ resizeEnd = new EventEmitter < number > ( ) ;
41
50
42
51
destroy$$ = new Subject < void > ( ) ;
52
+ containerElement : Element ;
43
53
44
54
private readonly element : HTMLElement ;
45
55
private initialWidth : number ;
46
56
private mouseDownScreenX : number ;
47
57
resizeHandle : HTMLElement ; // 在指令作用的元素上插入引导分割线,hover到作用元素后出现,拖动事件的宿主
48
58
resizeOverlay : HTMLElement ; // 在拖动时创建出一个透明的遮罩层,用以鼠标样式在拖动时总是拖动样式
49
59
resizeBar : HTMLElement ; // 拖动时的参考线,该参考线会被插入到 containerElement
50
- private mouseUpSubscription : Subscription ;
51
- private readonly document : Document ;
52
60
private handleHasUp : boolean ;
61
+ private readonly BAR_WIDTH = 2 ;
53
62
54
63
constructor (
55
64
element : ElementRef ,
56
65
readonly renderer2 : Renderer2 ,
57
- @Inject ( DOCUMENT )
58
- private readonly doc : Document ,
66
+ @Optional ( )
67
+ private readonly containerDirective : ContainerForResizableDirective ,
59
68
) {
60
69
this . element = element . nativeElement ;
61
- this . document = this . doc ;
70
+ this . containerElement =
71
+ this . containerDirective ?. el . nativeElement || this . element ;
62
72
}
63
73
64
74
ngAfterViewInit ( ) {
65
75
this . createResizeHandle ( ) ;
66
76
67
- this . binEvent ( this . element , 'mouseover' , ( ) => {
77
+ this . binEvent ( this . element , 'mouseover' ) . subscribe ( ( ) => {
68
78
this . handleHasUp = true ;
69
79
if ( ! this . resizeBar ) {
70
80
this . setResizeHandleVisible ( true ) ;
71
81
}
72
82
} ) ;
73
- this . binEvent ( this . element , 'mouseout' , ( ) => {
83
+ this . binEvent ( this . element , 'mouseout' ) . subscribe ( ( ) => {
74
84
this . handleHasUp = false ;
75
85
this . setResizeHandleVisible ( false ) ;
76
86
} ) ;
@@ -84,38 +94,36 @@ export class ResizableDirective implements AfterViewInit, OnDestroy {
84
94
private binEvent < E > (
85
95
target : HTMLElement | Document | Window ,
86
96
eventType : string ,
87
- callback : ( e : E ) => void ,
88
97
) {
89
- return fromEvent < E > ( target , eventType )
90
- . pipe ( takeUntil ( this . destroy$$ ) )
91
- . subscribe ( callback ) ;
98
+ return fromEvent < E > ( target , eventType ) . pipe ( takeUntil ( this . destroy$$ ) ) ;
92
99
}
93
100
94
101
private setResizeHandleVisible ( isVisible : boolean ) {
95
- this . resizeHandle &&
102
+ if ( this . resizeHandle ) {
96
103
this . renderer2 . setStyle (
97
104
this . resizeHandle ,
98
105
'visibility' ,
99
106
isVisible ? 'visible' : 'hidden' ,
100
107
) ;
108
+ }
101
109
}
102
110
103
111
private createResizeHandle ( ) {
104
- if ( ! this . resizeHandle ) {
105
- this . resizeHandle = this . renderer2 . createElement ( 'div' ) ;
106
- this . renderer2 . addClass ( this . resizeHandle , 'resize-handle' ) ;
112
+ if ( this . resizeHandle ) {
113
+ return ;
114
+ }
107
115
108
- this . binEvent ( this . resizeHandle , 'click' , ( e : Event ) =>
109
- e . stopPropagation ( ) ,
110
- ) ;
111
- this . binEvent (
112
- this . resizeHandle ,
113
- 'mousedown' ,
114
- this . onMousedown . bind ( this ) ,
115
- ) ;
116
+ this . resizeHandle = this . renderer2 . createElement ( 'div' ) ;
117
+ this . renderer2 . addClass ( this . resizeHandle , 'resize-handle' ) ;
116
118
117
- this . renderer2 . appendChild ( this . element , this . resizeHandle ) ;
118
- }
119
+ this . binEvent < Event > ( this . resizeHandle , 'click' ) . subscribe ( e =>
120
+ e . stopPropagation ( ) ,
121
+ ) ;
122
+ this . binEvent < MouseEvent > ( this . resizeHandle , 'mousedown' ) . subscribe ( e => {
123
+ this . onMousedown ( e ) ;
124
+ } ) ;
125
+
126
+ this . renderer2 . appendChild ( this . element , this . resizeHandle ) ;
119
127
}
120
128
121
129
private createResizeOverlay ( ) {
@@ -130,7 +138,7 @@ export class ResizableDirective implements AfterViewInit, OnDestroy {
130
138
this . renderer2 . setStyle (
131
139
this . resizeBar ,
132
140
'left' ,
133
- this . element . clientWidth + this . getOffset ( ) + 'px' ,
141
+ this . element . clientWidth + this . getOffset ( ) - this . BAR_WIDTH + 'px' ,
134
142
) ;
135
143
this . renderer2 . appendChild ( this . containerElement , this . resizeBar ) ;
136
144
}
@@ -143,42 +151,42 @@ export class ResizableDirective implements AfterViewInit, OnDestroy {
143
151
this . setResizeHandleVisible ( false ) ;
144
152
145
153
this . initialWidth = this . element . clientWidth ;
146
- this . resizeStartEvent . emit ( this . initialWidth ) ;
154
+ this . resizeStart . emit ( this . initialWidth ) ;
147
155
this . mouseDownScreenX = event . clientX ;
148
156
this . createResizeOverlay ( ) ;
149
157
this . createResizeBar ( ) ;
150
158
151
- this . mouseUpSubscription = this . binEvent < MouseEvent > (
159
+ const mouseMoveSubscription = this . binEvent < MouseEvent > (
152
160
document ,
153
- 'mouseup' ,
154
- ev => this . onMouseup ( ev ) ,
155
- ) ;
161
+ 'mousemove' ,
162
+ ) . subscribe ( e => {
163
+ this . move ( e ) ;
164
+ } ) ;
156
165
157
- this . binEvent ( document , 'mousemove' , this . move ) ;
166
+ const mouseUpSubscription = this . binEvent < MouseEvent > (
167
+ document ,
168
+ 'mouseup' ,
169
+ ) . subscribe ( ev => {
170
+ this . onMouseup ( ev ) ;
171
+ mouseUpSubscription . unsubscribe ( ) ;
172
+ mouseMoveSubscription . unsubscribe ( ) ;
173
+ } ) ;
158
174
}
159
175
160
176
private onMouseup ( event : MouseEvent ) : void {
161
177
this . handleHasUp && this . setResizeHandleVisible ( true ) ;
162
178
163
- const movementX = event . clientX - this . mouseDownScreenX ;
164
- const newWidth = this . initialWidth + movementX ;
165
- const finalWidth = this . getFinalWidth ( newWidth ) ;
166
-
167
- this . renderer2 . removeChild ( this . element , this . resizeOverlay ) ;
179
+ this . renderer2 . removeChild ( this . containerElement , this . resizeOverlay ) ;
168
180
this . resizeOverlay = null ;
169
181
this . renderer2 . removeChild ( this . containerElement , this . resizeBar ) ;
170
182
this . resizeBar = null ;
171
183
172
- this . resizeEndEvent . emit ( finalWidth ) ;
173
-
174
- if ( this . mouseUpSubscription && ! this . mouseUpSubscription . closed ) {
175
- this . _destroySubscription ( ) ;
176
- }
177
-
178
- this . document . removeEventListener ( 'mousemove' , this . move ) ;
184
+ const movementX = event . clientX - this . mouseDownScreenX ;
185
+ const newWidth = this . initialWidth + movementX ;
186
+ this . resizeEnd . emit ( this . getFinalWidth ( newWidth ) ) ;
179
187
}
180
188
181
- private readonly move = ( event : MouseEvent ) => {
189
+ private move ( event : MouseEvent ) {
182
190
if ( ! this . resizeBar ) {
183
191
return ;
184
192
}
@@ -189,14 +197,14 @@ export class ResizableDirective implements AfterViewInit, OnDestroy {
189
197
this . renderer2 . setStyle (
190
198
this . resizeBar ,
191
199
'left' ,
192
- `${ finalWidth + this . getOffset ( ) } px` ,
200
+ `${ finalWidth + this . getOffset ( ) - this . BAR_WIDTH } px` ,
193
201
) ;
194
- this . resizingEvent . emit ( finalWidth ) ;
195
- } ;
202
+ this . resizing . emit ( finalWidth ) ;
203
+ }
196
204
197
205
private getFinalWidth ( newWidth : number ) : number {
198
206
// 不能超出边界
199
- const _max = this . containerElement . clientWidth - this . getOffset ( ) ;
207
+ const _max = this . containerElement . clientWidth + this . getOffset ( ) ;
200
208
const minWidth = this . handleWidth ( this . minWidth || this . getOffset ( ) ) ;
201
209
202
210
const maxWidth = this . handleWidth ( this . maxWidth || _max ) ;
@@ -206,29 +214,21 @@ export class ResizableDirective implements AfterViewInit, OnDestroy {
206
214
private getOffset ( ) {
207
215
return (
208
216
this . element . getBoundingClientRect ( ) . left -
209
- this . containerElement . getBoundingClientRect ( ) . left -
210
- 2 // 2 是resizeBar的宽度
217
+ this . containerElement . getBoundingClientRect ( ) . left
211
218
) ;
212
219
}
213
220
214
221
private handleWidth ( width : string | number ) {
215
- if ( ! width ) {
216
- return ;
217
- }
218
222
if ( typeof width === 'number' ) {
219
223
return width ;
220
224
}
225
+ if ( ! width ) {
226
+ return ;
227
+ }
221
228
if ( width . includes ( '%' ) ) {
222
229
const tableWidth = this . containerElement . clientWidth ;
223
230
return ( tableWidth * Number . parseFloat ( width ) ) / 100 ;
224
231
}
225
232
return Number . parseFloat ( width . replace ( / \D + / , '' ) ) ;
226
233
}
227
-
228
- private _destroySubscription ( ) {
229
- if ( this . mouseUpSubscription ) {
230
- this . mouseUpSubscription . unsubscribe ( ) ;
231
- this . mouseUpSubscription = null ;
232
- }
233
- }
234
234
}
0 commit comments