概要
Radix UIのToastでSnackbarの実装をメモする
詳細
- Snackbar.tsx
import { type ReactNode } from 'react';
import * as Toast from '@radix-ui/react-toast';
import styles from './Snackbar.module.css';
type Props = {
/** 通知を表示するか */
open: boolean;
/** 通知メッセージ(文字列、またはReactコンポーネント) */
message: ReactNode;
/** 通知表示の継続時間(単位:ミリ秒) */
duration?: number;
/** 通知表示の状態が変更されたときのコールバック */
onOpenChange: (open: boolean) => void;
};
/**
* スナックバーのコンポーネント
* 全画面の下部に通知メッセージを表示して、一定時間後に自動的に非表示になる
*/
export function Snackbar({ open, message, duration = 3000, onOpenChange }: Props) {
const messageNode =
typeof message === 'string' ? <p className={styles.defaultNode}>{message}</p> : message;
return (
<Toast.Provider duration={duration} swipeDirection="down">
<Toast.Root className={styles.toastRoot} open={open} onOpenChange={onOpenChange}>
<Toast.Description asChild>{messageNode}</Toast.Description>
</Toast.Root>
<Toast.Viewport />
</Toast.Provider>
);
}
- Snackbar.module.css
/* layer定義により共通スタイルの優先度を下げて、layer未定義の利用側のスタイルに負けるようにする */
@layer snackbarComponent {
.defaultNode {
padding: 16px;
font-size: 14px;
font-weight: bold;
color: #ffffff;
}
.toastRoot {
position: fixed;
bottom: 30px;
left: 50%;
z-index: 999;
background-color: #26303b;
border-radius: 4px;
box-shadow: 0 4px 4px 0 rgba(38, 48, 59, 0.4);
opacity: 0.95;
list-style: none;
transform: translateX(-50%);
&[data-state='open'] {
animation: fadein 0.3s;
}
&[data-state='closed'] {
opacity: 0;
animation: fadeout 0.3s;
}
}
@keyframes fadein {
from {
bottom: 0;
opacity: 0;
}
to {
bottom: 30px;
opacity: 1;
}
}
@keyframes fadeout {
from {
bottom: 30px;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}
}