mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
Some checks are pending
* test(web-shell): add browser and lint harness * test(web-shell): harden browser smoke harness * fix(web-shell): guard mock daemon model state * test(web-shell): remove unused scenario harness * fix(web-shell): remove stale lint disables * test(web-shell): make matchMedia stub writable * fix(web-shell): exclude tests from package typecheck * test(web-shell): tighten mock daemon route contract * Update packages/web-shell/client/e2e/utils/mockDaemon.ts Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> * test(web-shell): clear stale SSE connections * ci(web-shell): gate smoke on full CI profile --------- Co-authored-by: ermin.zem <ermin.zem@alibaba-inc.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: 易良 <1204183885@qq.com>
77 lines
1.7 KiB
TypeScript
77 lines
1.7 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useI18n } from '../i18n';
|
|
import styles from './ToastHost.module.css';
|
|
|
|
export type ToastTone = 'info' | 'warning' | 'error' | 'success';
|
|
|
|
export interface WebShellToast {
|
|
id: string;
|
|
tone: ToastTone;
|
|
message: string;
|
|
}
|
|
|
|
interface ToastHostProps {
|
|
toasts: readonly WebShellToast[];
|
|
onDismiss: (id: string) => void;
|
|
autoDismissMs?: number;
|
|
}
|
|
|
|
export function ToastHost({
|
|
toasts,
|
|
onDismiss,
|
|
autoDismissMs = 5000,
|
|
}: ToastHostProps) {
|
|
if (toasts.length === 0) return null;
|
|
return (
|
|
<div
|
|
className={styles.host}
|
|
role="status"
|
|
aria-live="polite"
|
|
data-web-shell-toast-host
|
|
>
|
|
{toasts.map((toast) => (
|
|
<ToastItem
|
|
key={toast.id}
|
|
toast={toast}
|
|
onDismiss={onDismiss}
|
|
autoDismissMs={autoDismissMs}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ToastItem({
|
|
toast,
|
|
onDismiss,
|
|
autoDismissMs,
|
|
}: {
|
|
toast: WebShellToast;
|
|
onDismiss: (id: string) => void;
|
|
autoDismissMs: number;
|
|
}) {
|
|
const { t } = useI18n();
|
|
useEffect(() => {
|
|
const timer = window.setTimeout(() => onDismiss(toast.id), autoDismissMs);
|
|
return () => window.clearTimeout(timer);
|
|
}, [autoDismissMs, onDismiss, toast.id]);
|
|
|
|
return (
|
|
<div
|
|
className={`${styles.toast} ${styles[toast.tone]}`}
|
|
data-web-shell-toast
|
|
data-tone={toast.tone}
|
|
>
|
|
<div className={styles.message}>{toast.message}</div>
|
|
<button
|
|
type="button"
|
|
className={styles.close}
|
|
onClick={() => onDismiss(toast.id)}
|
|
aria-label={t('toast.dismiss')}
|
|
title={t('toast.dismissShort')}
|
|
>
|
|
x
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|