Skip to content

Commit

Permalink
Merge pull request #105 from js-tool-pack/popover_click_escape
Browse files Browse the repository at this point in the history
feat(components/popover): 当触发方式为 click 时,可按下 Esc 键关闭
  • Loading branch information
mengxinssfd authored Apr 4, 2024
2 parents 462404d + 65fb83a commit a52e400
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 6 deletions.
21 changes: 21 additions & 0 deletions packages/components/src/popover/__tests__/Popover.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ describe('Popover', () => {
expect(document.body).toMatchSnapshot();
});

test('click and keydown escape', () => {
const { container } = render(
<Popover trigger="click" content="click">
<Button>click触发</Button>
</Popover>,
);

expect(getBalloon()).toBeNull();

fireEvent.click(container.querySelector('button')!);
act(() => jest.advanceTimersByTime(0));
expect(getBalloon()).not.toBeNull();
expect(getBalloon()).toHaveClass('t-popover-enter-active');
act(() => jest.advanceTimersByTime(500));
expect(getBalloon()).not.toHaveClass('t-popover-enter-active');

fireEvent.keyDown(window, { code: 'Escape' });
act(() => jest.advanceTimersByTime(500));
expect(getBalloon()).toHaveClass('t-transition--invisible');
});

test('click disabled', () => {
const { container } = render(
<Popover trigger="click" content="click" disabled>
Expand Down
31 changes: 25 additions & 6 deletions packages/components/src/popover/hooks/useShowController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,41 @@ export function useShowController(
leaveBalloonSubject,
);
case 'click':
// 点击触发元素
const triggerClick$ = fromEvent(el, 'click').pipe(
// 因为 react 的合成事件是使用的事件委托机制,比直接监听 dom 的事件回调执行的要慢一步,所以加上延迟
delay(0),
// 排除被拦截的事件
filter((e) => !e.defaultPrevented),
);
const outerEvent = fromOuterEvent(
// 按下 Esc 键
const keydownEscape$ = fromEvent<KeyboardEvent>(
window,
'keydown',
).pipe(filter((e) => e.code === 'Escape'));
// 点击除触发器与窗体之外的dom
const outerClick$ = fromOuterEvent(
() => [el, balloonElRef.current],
'click',
).pipe(tap(close), takeUntil(triggerClick$), take(1));
const queueEvent = triggerClick$.pipe(
switchMap(() => toggle().pipe(takeWhile((v) => v))),
switchMap(() => outerEvent),
);
// 关闭序列
const closeWaiter$ = merge(outerClick$, keydownEscape$).pipe(
tap(close),
takeUntil(triggerClick$),
take(1),
);
// 开启序列
const openWaiter$ = triggerClick$.pipe(
switchMap(() =>
// 监听 show 的变化,当 show 为 true 时结束监听并把控制权交给下一位
toggle().pipe(takeWhile((v) => v)),
),
switchMap(() => closeWaiter$),
);
// 当弹窗已经打开时(例如Button loading时会刷新该effect),添加点击和外部点击订阅
// 不然只有点击触发器才能外部点击订阅,否则如果很多popover的话会有一堆外部点击订阅
const clickSub = (
show ? merge(outerEvent, queueEvent) : queueEvent
show ? merge(closeWaiter$, openWaiter$) : openWaiter$
).subscribe();
return clickSub.unsubscribe.bind(clickSub);
case 'focus':
Expand Down

0 comments on commit a52e400

Please sign in to comment.