Skip to content
40 changes: 34 additions & 6 deletions semcore/notice-bubble/src/NoticeBubble.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Animation, Box, Flex, Portal } from '@semcore/base-components';
import Button from '@semcore/button';
import { createComponent, Component, sstyled, Root } from '@semcore/core';
import { createComponent, Component, sstyled, Root, lastInteraction } from '@semcore/core';
import type { Intergalactic } from '@semcore/core';
import type { WithI18nEnhanceProps } from '@semcore/core/lib/utils/enhances/i18nEnhance';
import i18nEnhance from '@semcore/core/lib/utils/enhances/i18nEnhance';
Expand All @@ -9,7 +9,7 @@ import { getFocusableIn } from '@semcore/core/lib/utils/focus-lock/getFocusableI
import isNode from '@semcore/core/lib/utils/isNode';
import { useForkRef } from '@semcore/core/lib/utils/ref';
import { contextThemeEnhance } from '@semcore/core/lib/utils/ThemeProvider';
import { useFocusLock, setFocus } from '@semcore/core/lib/utils/use/useFocusLock';
import { setFocus, isFocusInside, useFocusLock } from '@semcore/core/lib/utils/use/useFocusLock';
import { cssVariableEnhance } from '@semcore/core/lib/utils/useCssVariable';
import {
ZIndexStackingContextProvider,
Expand Down Expand Up @@ -206,6 +206,7 @@ class ViewInfo extends Component<NoticeBubbleViewItemProps> {
timer: Timer | null = null;
ref = React.createRef<HTMLDivElement>();
closeButtonRef = React.createRef<HTMLButtonElement>();
triggerButtonComponent: Element | null = null;

componentDidMount() {
const { duration } = this.props;
Expand All @@ -214,24 +215,38 @@ class ViewInfo extends Component<NoticeBubbleViewItemProps> {
document.body.addEventListener('mousemove', this.handleBodyMouseMove);
}

this.triggerButtonComponent = document.activeElement;

const noticeElement = this.ref.current;

if (noticeElement) {
const focusableNodes = getFocusableIn(noticeElement).filter(
const allFocusableNodes = getFocusableIn(noticeElement);
const focusableNodes = allFocusableNodes.filter(
(node) => node !== this.closeButtonRef.current,
);

if (focusableNodes.length > 0) {
if (focusableNodes.length > 0 || (!duration && allFocusableNodes.length > 0)) {
setTimeout(() => setFocus(noticeElement), 0);
}
}
}

componentWillUnmount() {
const triggerElement = this.triggerButtonComponent;
if (this.ref.current && isFocusInside(this.ref.current) && triggerElement instanceof HTMLElement) {
setTimeout(() => setFocus(triggerElement), 0);
}

this.clearTimer();
document.body.removeEventListener('mousemove', this.handleBodyMouseMove);
}

isFocusInBubble() {
const noticeElement = this.ref.current;

return noticeElement ? isFocusInside(noticeElement) : false;
}

clearTimer() {
if (this.timer) {
this.timer.clear();
Expand All @@ -251,18 +266,28 @@ class ViewInfo extends Component<NoticeBubbleViewItemProps> {
}
};

handleFocus = () => {
if (!this.timer) return;
this.timer.pause();
};

handleBlur = () => {
if (!this.timer) return;
this.timer.resume();
};

handleMouseEnter = () => {
if (!this.timer) return;
this.timer.pause();
};

handleMouseLeave = () => {
if (!this.timer) return;
if (!this.timer || this.isFocusInBubble()) return;
this.timer.resume();
};

handleBodyMouseMove = (event: MouseEvent) => {
if (!this.timer?.paused) return;
if (!this.timer?.paused || this.isFocusInBubble()) return;
const rect = this.ref.current?.getBoundingClientRect();
if (!rect) return;
const mouseInRect =
Expand Down Expand Up @@ -303,10 +328,13 @@ class ViewInfo extends Component<NoticeBubbleViewItemProps> {
keyframes={[styles['@enter'], styles['@exit']]}
>
<SBubble
render={Flex}
type={type}
ref={this.ref}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onKeyDown={this.handleKeydown}
role={type === 'warning' ? 'alert' : undefined}
focusLock={focusLock}
Expand Down
2 changes: 2 additions & 0 deletions semcore/notice-bubble/src/NoticeBubble.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export type NoticeBubbleProps = BoxProps & {
* If enabled, browser focus will be locked in the notice
* until it's closed. After close focus should return to the element
* where it was placed before notice appear.
*
* @deprecated
*/
focusLock?: boolean;
/**
Expand Down
Loading