Skip to content

Commit 65fb83a

Browse files
committed
feat(components/popover): 当触发方式为 click 时,可按下 Esc 键关闭
1 parent 462404d commit 65fb83a

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

packages/components/src/popover/__tests__/Popover.test.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ describe('Popover', () => {
9999
expect(document.body).toMatchSnapshot();
100100
});
101101

102+
test('click and keydown escape', () => {
103+
const { container } = render(
104+
<Popover trigger="click" content="click">
105+
<Button>click触发</Button>
106+
</Popover>,
107+
);
108+
109+
expect(getBalloon()).toBeNull();
110+
111+
fireEvent.click(container.querySelector('button')!);
112+
act(() => jest.advanceTimersByTime(0));
113+
expect(getBalloon()).not.toBeNull();
114+
expect(getBalloon()).toHaveClass('t-popover-enter-active');
115+
act(() => jest.advanceTimersByTime(500));
116+
expect(getBalloon()).not.toHaveClass('t-popover-enter-active');
117+
118+
fireEvent.keyDown(window, { code: 'Escape' });
119+
act(() => jest.advanceTimersByTime(500));
120+
expect(getBalloon()).toHaveClass('t-transition--invisible');
121+
});
122+
102123
test('click disabled', () => {
103124
const { container } = render(
104125
<Popover trigger="click" content="click" disabled>

packages/components/src/popover/hooks/useShowController.ts

+25-6
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,41 @@ export function useShowController(
133133
leaveBalloonSubject,
134134
);
135135
case 'click':
136+
// 点击触发元素
136137
const triggerClick$ = fromEvent(el, 'click').pipe(
138+
// 因为 react 的合成事件是使用的事件委托机制,比直接监听 dom 的事件回调执行的要慢一步,所以加上延迟
137139
delay(0),
140+
// 排除被拦截的事件
138141
filter((e) => !e.defaultPrevented),
139142
);
140-
const outerEvent = fromOuterEvent(
143+
// 按下 Esc 键
144+
const keydownEscape$ = fromEvent<KeyboardEvent>(
145+
window,
146+
'keydown',
147+
).pipe(filter((e) => e.code === 'Escape'));
148+
// 点击除触发器与窗体之外的dom
149+
const outerClick$ = fromOuterEvent(
141150
() => [el, balloonElRef.current],
142151
'click',
143-
).pipe(tap(close), takeUntil(triggerClick$), take(1));
144-
const queueEvent = triggerClick$.pipe(
145-
switchMap(() => toggle().pipe(takeWhile((v) => v))),
146-
switchMap(() => outerEvent),
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$),
147166
);
148167
// 当弹窗已经打开时(例如Button loading时会刷新该effect),添加点击和外部点击订阅
149168
// 不然只有点击触发器才能外部点击订阅,否则如果很多popover的话会有一堆外部点击订阅
150169
const clickSub = (
151-
show ? merge(outerEvent, queueEvent) : queueEvent
170+
show ? merge(closeWaiter$, openWaiter$) : openWaiter$
152171
).subscribe();
153172
return clickSub.unsubscribe.bind(clickSub);
154173
case 'focus':

0 commit comments

Comments
 (0)