NoticeBubble
TIP
Each example uses its own instance of NoticeBubbleManager
, which is why notices from different examples can overlay each other.
Basic notice
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import Link from '@semcore/link';
const manager = new NoticeBubbleManager();
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const handleClick = () => {
manager.add({
children: (
<>
Link was moved to <Link href='#'>Cats from outer space group</Link>
</>
),
initialAnimation: true,
duration: 0,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<>
<Button onClick={handleClick} ref={openButtonRef}>Show basic notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
NoticeBubble not in portal
If NoticeBubbleContainer has disablePortal
it will add position: sticky
to Bubbles
.
Parent should have position: relative
and overflow
with scroll.
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import Link from '@semcore/link';
const manager = new NoticeBubbleManager();
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const handleClick = () => {
manager.add({
children: (
<>
Link was moved to <Link href='#'>Cats from outer space group</Link>
</>
),
initialAnimation: true,
duration: 300000,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<div
style={{ border: '1px dashed #eee', height: '180px', position: 'relative', overflow: 'auto' }}
>
<div style={{ height: '800px' }}>
<NoticeBubbleContainer manager={manager} disablePortal={true} />
<Button onClick={handleClick} m={5} ref={openButtonRef}>
Show basic notice
</Button>
</div>
</div>
);
};
export default Demo;
Undo action
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import Link from '@semcore/link';
const manager = new NoticeBubbleManager();
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const handleClick = () => {
manager.add({
children: (
<>
Link was moved to <Link href='#'>Cats from outer space group</Link>
</>
),
action: <Button theme='invert'>Undo</Button>,
initialAnimation: true,
duration: 0,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<>
<Button onClick={handleClick} ref={openButtonRef}>
Show notice with undo action
</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Reload action
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import ReloadM from '@semcore/icon/Reload/m';
const manager = new NoticeBubbleManager();
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const handleClick = () => {
manager.add({
children: 'Data for 5 new profiles is ready. Please reload the page to view it.',
action: (
<Button theme='invert' addonLeft={ReloadM}>
Reload the page
</Button>
),
initialAnimation: true,
duration: 0,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<>
<Button onClick={handleClick} ref={openButtonRef}>
Show notice with reload action
</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Completion state
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import { Flex } from '@semcore/flex-box';
import CheckM from '@semcore/icon/Check/m';
const manager = new NoticeBubbleManager();
const Demo = () => {
const handleClick = () => {
manager.add({
children: (
<Flex justifyContent='center' alignItems='center' gap={1}>
<CheckM color='--intergalactic-icon-primary-success' />
Undone
</Flex>
),
initialAnimation: true,
duration: 4000,
});
};
return (
<>
<Button onClick={handleClick}>Show notice with completion state</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Success notice
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import CheckM from '@semcore/icon/Check/m';
const manager = new NoticeBubbleManager();
const Demo = () => {
const handleClick = () => {
manager.add({
icon: <CheckM color='--intergalactic-icon-primary-success' />,
children: 'Keyword was successfully moved to Keyword Analyzer!',
initialAnimation: true,
duration: 5000,
});
};
return (
<>
<Button onClick={handleClick}>Show success notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Failure notice
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import WarningM from '@semcore/icon/Warning/m';
import ReloadM from '@semcore/icon/Reload/m';
const manager = new NoticeBubbleManager();
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const handleClick = () => {
manager.add({
children: 'Unfortunately, your recent changes were not saved. Try again later.',
icon: <WarningM color='--intergalactic-icon-primary-warning' />,
action: (
<Button theme='invert' addonLeft={ReloadM}>
Reload the page
</Button>
),
initialAnimation: true,
duration: 0,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<>
<Button onClick={handleClick} ref={openButtonRef}>Show failure notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Loading state
Activate the Try again button in the notice to see the loading state.
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import WarningM from '@semcore/icon/Warning/m';
import ReloadM from '@semcore/icon/Reload/m';
import Spin from '@semcore/spin';
import { Flex } from '@semcore/flex-box';
const manager = new NoticeBubbleManager();
let notice: any = null;
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const tryAgain = async () => {
if (!notice) return;
notice.update({
icon: null,
children: (
<Flex justifyContent='center' gap={1}>
<Spin size='xs' theme='invert' />
Loading...
</Flex>
),
action: null,
});
await new Promise((resolve) => setTimeout(resolve, 1500));
notice.update({
children: 'Unfortunately, your recent changes were not saved. Try again later.',
icon: <WarningM color='--intergalactic-icon-primary-warning' />,
action: (
<Button theme='invert' onClick={tryAgain} addonLeft={ReloadM}>
Try again
</Button>
),
});
};
const handleClick = async () => {
if (notice) {
notice.remove();
await new Promise((resolve) => setTimeout(resolve, 500));
}
notice = manager.add({
children: 'Unfortunately, your recent changes were not saved. Try again later.',
icon: <WarningM color='--intergalactic-icon-primary-warning' />,
action: (
<Button theme='invert' onClick={tryAgain} addonLeft={ReloadM}>
Try again
</Button>
),
initialAnimation: true,
duration: 20000,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<>
<Button onClick={handleClick} ref={openButtonRef}>Show dynamic notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Special event notice
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import MailSent from '@semcore/illustration/MailSent';
import { Flex } from '@semcore/flex-box';
const manager = new NoticeBubbleManager();
const Demo = () => {
const handleClick = () => {
manager.add({
children: (
<Flex gap={4}>
<MailSent style={{ flexShrink: 0 }} />
Your post is on its way, and we will take great care of it!
</Flex>
),
initialAnimation: true,
duration: 10000,
});
};
return (
<>
<Button onClick={handleClick}>Show special event notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
No connection
Use type="warning"
for this case.
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import Spin from '@semcore/spin';
const manager = new NoticeBubbleManager();
const Demo = () => {
const handleClick = () => {
manager.add({
icon: <Spin size='xs' theme='invert' />,
children: 'Server connection lost. Reconnecting...',
type: 'warning',
initialAnimation: true,
duration: 10000,
});
};
return (
<>
<Button onClick={handleClick}>Show no connection notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
No connection with action
Use type="warning"
for this case.
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
import ReloadM from '@semcore/icon/Reload/m';
const manager = new NoticeBubbleManager();
const Demo = () => {
const openButtonRef = React.useRef<HTMLButtonElement>(null);
const handleClick = () => {
manager.add({
children: 'Server connection lost. Check your internet connection and reload the page.',
action: (
<Button theme='invert' addonLeft={ReloadM}>
Reload the page
</Button>
),
type: 'warning',
initialAnimation: true,
duration: 0,
onClose: () => {
setTimeout(() => {
openButtonRef.current?.focus();
}, 300);
},
});
};
return (
<>
<Button onClick={handleClick} ref={openButtonRef}>Show no connection notice with action</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;
Replace previous notice
Press the button several times to replace the previous notice.
WARNING
Use this API only if there's enough time between events, so that all notices have enough time to appear and be read by the user, or if missing some notices isn't critical.
tsx
import React from 'react';
import { NoticeBubbleContainer, NoticeBubbleManager } from '@semcore/notice-bubble';
import Button from '@semcore/button';
const manager = new NoticeBubbleManager();
let counter = 0;
const Demo = () => {
const handleClick = () => {
counter++;
manager.replaceLast({
children: `Link ${counter} was moved to "Cats from outer space"`,
initialAnimation: true,
duration: 0,
});
};
return (
<>
<Button onClick={handleClick}>Show basic notice</Button>
<NoticeBubbleContainer manager={manager} />
</>
);
};
export default Demo;