1
1
/*eslint no-case-declarations: "off"*/
2
- import {
3
- AsyncSubject ,
4
- fromEvent ,
5
- switchMap ,
6
- takeUntil ,
7
- takeWhile ,
8
- Subject ,
9
- filter ,
10
- delay ,
11
- merge ,
12
- take ,
13
- tap ,
14
- of ,
15
- } from 'rxjs' ;
16
2
import React , { useImperativeHandle , useEffect , useState , useRef } from 'react' ;
17
3
import { PopoverRequiredPartProps } from '~/popover/Popover' ;
18
- import { fromOuterEvent , useNextEffect } from '@pkg/shared' ;
4
+ import { handleHover } from '~/popover/hooks/handleHover' ;
5
+ import { handleFocus } from '~/popover/hooks/handleFocus' ;
6
+ import { handleClick } from '~/popover/hooks/handleClick' ;
19
7
import { castArray , emptyFn } from '@tool-pack/basic' ;
20
8
import { collectScroller } from '@tool-pack/dom' ;
21
-
22
- function hoverTriggerHandler (
23
- triggerEl : HTMLElement ,
24
- open : ( ) => void ,
25
- close : ( ) => void ,
26
- enterDelay : number ,
27
- leaveDelay : number ,
28
- show : boolean ,
29
- enterBalloonSubject : React . MutableRefObject < Subject < void > > ,
30
- leaveBalloonSubject : React . MutableRefObject < Subject < void > > ,
31
- ) {
32
- const triggerEnterEvent = fromEvent ( triggerEl , 'mouseenter' ) ;
33
- const triggerMoveEvent = fromEvent ( triggerEl , 'mousemove' ) ;
34
- const triggerLeaveEvent = fromEvent ( triggerEl , 'mouseleave' ) ;
35
-
36
- const leaveEvent = merge (
37
- triggerLeaveEvent ,
38
- leaveBalloonSubject . current . asObservable ( ) ,
39
- )
40
- . pipe (
41
- switchMap ( ( ) =>
42
- of ( null ) . pipe (
43
- delay ( leaveDelay ) ,
44
- takeUntil ( triggerMoveEvent ) ,
45
- takeUntil ( enterBalloonSubject . current . asObservable ( ) ) ,
46
- ) ,
47
- ) ,
48
- takeUntil ( triggerEnterEvent ) ,
49
- take ( 1 ) ,
50
- )
51
- . pipe ( tap ( close ) ) ;
52
-
53
- const enterEvent = triggerEnterEvent . pipe (
54
- switchMap ( ( ) =>
55
- enterDelay
56
- ? of ( null ) . pipe ( delay ( enterDelay ) , takeUntil ( triggerLeaveEvent ) )
57
- : of ( null ) ,
58
- ) ,
59
- tap ( open ) ,
60
- switchMap ( ( ) => leaveEvent ) ,
61
- ) ;
62
-
63
- const sub = ( show ? merge ( leaveEvent , enterEvent ) : enterEvent ) . subscribe ( ) ;
64
- return sub . unsubscribe . bind ( sub ) ;
65
- }
9
+ import { AsyncSubject , Subject } from 'rxjs' ;
10
+ import { useNextEffect } from '@pkg/shared' ;
66
11
67
12
export function useShowController (
68
13
triggerElRef : React . RefObject < HTMLElement > ,
@@ -122,7 +67,7 @@ export function useShowController(
122
67
const cancellers : Array < ( ) => void > = triggers . map ( ( t ) => {
123
68
switch ( t ) {
124
69
case 'hover' :
125
- return hoverTriggerHandler (
70
+ return handleHover (
126
71
el ,
127
72
open ,
128
73
close ,
@@ -133,49 +78,9 @@ export function useShowController(
133
78
leaveBalloonSubject ,
134
79
) ;
135
80
case 'click' :
136
- // 点击触发元素
137
- const triggerClick$ = fromEvent ( el , 'click' ) . pipe (
138
- // 因为 react 的合成事件是使用的事件委托机制,比直接监听 dom 的事件回调执行的要慢一步,所以加上延迟
139
- delay ( 0 ) ,
140
- // 排除被拦截的事件
141
- filter ( ( e ) => ! e . defaultPrevented ) ,
142
- ) ;
143
- // 按下 Esc 键
144
- const keydownEscape$ = fromEvent < KeyboardEvent > (
145
- window ,
146
- 'keydown' ,
147
- ) . pipe ( filter ( ( e ) => e . code === 'Escape' ) ) ;
148
- // 点击除触发器与窗体之外的dom
149
- const outerClick$ = fromOuterEvent (
150
- ( ) => [ el , balloonElRef . current ] ,
151
- 'click' ,
152
- ) ;
153
- // 关闭序列
154
- const closeWaiter$ = merge ( outerClick$ , keydownEscape$ ) . pipe (
155
- tap ( close ) ,
156
- takeUntil ( triggerClick$ ) ,
157
- take ( 1 ) ,
158
- ) ;
159
- // 开启序列
160
- const openWaiter$ = triggerClick$ . pipe (
161
- switchMap ( ( ) =>
162
- // 监听 show 的变化,当 show 为 true 时结束监听并把控制权交给下一位
163
- toggle ( ) . pipe ( takeWhile ( ( v ) => v ) ) ,
164
- ) ,
165
- switchMap ( ( ) => closeWaiter$ ) ,
166
- ) ;
167
- // 当弹窗已经打开时(例如Button loading时会刷新该effect),添加点击和外部点击订阅
168
- // 不然只有点击触发器才能外部点击订阅,否则如果很多popover的话会有一堆外部点击订阅
169
- const clickSub = (
170
- show ? merge ( closeWaiter$ , openWaiter$ ) : openWaiter$
171
- ) . subscribe ( ) ;
172
- return clickSub . unsubscribe . bind ( clickSub ) ;
81
+ return handleClick ( el , close , show , balloonElRef , toggle ) ;
173
82
case 'focus' :
174
- const focusSub = merge (
175
- fromEvent ( el , 'focus' ) . pipe ( tap ( open ) ) ,
176
- fromEvent ( el , 'blur' ) . pipe ( tap ( close ) ) ,
177
- ) . subscribe ( ) ;
178
- return focusSub . unsubscribe . bind ( focusSub ) ;
83
+ return handleFocus ( el , close , open , show ) ;
179
84
case 'contextmenu' :
180
85
// eslint-disable-next-line @typescript-eslint/no-empty-function
181
86
return ( ) => { } ;
0 commit comments