Files
MetonaToast/README.md
T
tianhao bbff631f4f release: v0.4.0 — React 适配器、loading链式id稳定、dragThreshold、RTL适配、dedupe插件
- React 适配器子包:@metona-team/metona-toast/react — useToast() hook + 声明式 <Toast /> 组件,组件卸载自动清理;react 为 optional peerDependency,主包保持零依赖
- loading 链式转换改原地 update:id 稳定不重建 DOM,转换后 duration 恢复默认自动关闭;update() 支持 duration 变更动态重启计时器
- _emit 幽灵实例修复:beforeShow 拦截后的 toast 不再注册进 _toasts
- dragThreshold 配置项(默认 120px,原硬编码);RTL 容器 dir 属性 + 进度条/side 条/色条镜像
- dedupe 预设插件:相同 type+message 自动去重,支持 uninstall
- jest 环境隔离测试独立成 tests/ssr.test.ts(resetModules 不再污染共享模块状态);新增 tests/setup.ts 补 TextEncoder
- 文档:README/docs.html 补 React 适配器、dedupe、dragThreshold;CHANGELOG 更新;328 测试全过
2026-08-08 14:45:19 +08:00

9.3 KiB
Raw Blame History

MetonaToast

轻量、零依赖、精致美观的 Toast 通知库。

npm version bundle size license

特性

  • 零依赖 — TypeScript 严格模式源码,gzip 后不到 10KB
  • 107 种图标类型 — success/error/warning/info/loading 及更多扩展类型,覆盖所有常见场景
  • 11 种动画 — slide/fade/scale/bounce/flip/rotate/zoom/slideUp/slideDown/slideLeft/slideRight,每种效果明显不同
  • 主题系统 — light/dark/auto/warm 四种内置主题,支持 registerTheme() 注册自定义主题
  • 国际化 — 内置 zh-CN / en-US 完整翻译(去重优化),可通过 addTranslations() 扩展
  • 插件系统 — 4 款预设插件 (keyboard/persistence/accessibility/dedupe) + 自定义插件
  • 可拖拽关闭 — 拖动 Toast 任意方向即可关闭,阈值可配置
  • TypeScript — 严格模式,源码级类型安全,类型从源码自动生成无手动维护
  • 全局错误回调onError 捕获所有钩子异常和定时器错误,方便接入监控系统

安装

npm install @metona-team/metona-toast

引用方式

// ES Module
import MeToast from '@metona-team/metona-toast';

// CommonJS
const MeToast = require('@metona-team/metona-toast');
<!-- CDN UMD 开发版 -->
<script src="https://git.metona.cn/MetonaTeam/MetonaToast/raw/branch/master/dist/metona-toast.js"></script>

<!-- CDN 压缩版(生产环境推荐) -->
<script src="https://git.metona.cn/MetonaTeam/MetonaToast/raw/branch/master/dist/metona-toast.min.js"></script>

<!-- CDN ES Module -->
<script type="module">
  import MeToast from 'https://git.metona.cn/MetonaTeam/MetonaToast/raw/branch/master/dist/metona-toast.esm.js';
</script>

环境与版本支持

环境 最低版本
Chrome 64+
Firefox 63+
Safari 11+
Edge 79+
Opera 51+
iOS Safari 11+
Android Chrome 64+
Node.js 16+ESM & CJS 双格式)
TypeScript 4.5+(完整类型声明)

核心依赖:Web Animations APIChrome 64+, Firefox 63+, Safari 11+)、Pointer Events、CSS Grid、CSS Custom Properties。

快速开始

浏览器中 window 上会注册两个全局变量:MeToastMet,两者完全等价,任选其一使用。

import MeToast from '@metona-team/metona-toast';

// 也可以使用 Met — 与 MeToast 完全等价
Met.success('使用 Met 同样可用');

// 基础用法
MeToast.success('操作成功!');
MeToast.error('操作失败!');
MeToast.warning('请注意检查');
MeToast.info('系统维护中');

// 带标题
MeToast.success({ title: '保存成功', message: '文件已同步到云端' });

// 加载 → 成功链式转换(原地更新同一实例,id 稳定)
const loading = MeToast.loading('正在提交...');
const toast = await wait();
loading.success('提交成功!');
// toast.id === loading.id

// Promise 风格
await MeToast.promise(fetch('/api/data'), {
  loading: '加载中...',
  success: '加载完成!',
  error: '加载失败',
});

全局配置

MeToast.configure({
  position: 'top-right',    // top-left | top-center | top-right | bottom-left | bottom-center | bottom-right
  duration: 4000,           // 显示时长(ms),0 为不自动关闭
  max: 6,                   // 同时最多显示条数
  theme: 'auto',            // light | dark | auto | warm
  animation: 'slide',       // slide | fade | scale | bounce | flip | rotate | zoom | slideUp | slideDown | slideLeft | slideRight
  pauseOnHover: true,       // 悬停暂停计时
  closeOnClick: true,       // 点击关闭
  draggable: true,          // 允许拖拽关闭
  dragThreshold: 120,       // 拖拽关闭阈值(px)
  showProgress: true,       // 显示进度条
  draggable: true,          // 允许拖拽关闭
  locale: 'zh-CN',          // 语言
  onError: ({ hook, error, toast }) => {  // 全局错误回调(钩子/定时器异常)
    console.error('Toast error:', hook, error);
  },
});

动画效果

11 种动画,每种感官差异明显:

动画 效果描述
slide 从右侧滑入 + 轻微过冲回弹
fade 纯淡入(blur→清晰),从容优雅
scale 弹性放大(0.55→1.07→1.0
bounce 从天而降四段弹跳
flip 3D 翻转入场 + 回摆
rotate 旋转摇摆进入
zoom 从中心爆发式弹出
slideUp 从下方弹入
slideDown 从上方弹入
slideLeft 从左侧滑入
slideRight 从右侧滑入
MeToast.success('弹跳动画', { animation: 'bounce' });

// 注册自定义动画
MeToast.animations.register('myAnim', {
  enter: { transform: 'rotate(-30deg) scale(0.5)', opacity: 0 },
  leave: { transform: 'rotate(0) scale(1)', opacity: 1 },
  duration: 500,
  easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
});

主题

// 切换主题
MeToast.themes.switchTheme('dark');
MeToast.themes.switchTheme('warm');

// 注册自定义主题
MeToast.themes.registerTheme('ocean', {
  bg: 'rgba(240, 249, 255, 0.96)',
  text: '#0c4a6e',
  border: 'rgba(14, 165, 233, 0.2)',
  shadow: '0 10px 36px -10px rgba(14, 165, 233, 0.18)',
  hoverShadow: '0 14px 48px -10px rgba(14, 165, 233, 0.22)',
  progressBg: 'rgba(14, 165, 233, 0.1)',
  closeHoverBg: 'rgba(14, 165, 233, 0.1)',
});
MeToast.themes.switchTheme('ocean');

高级功能

// 确认对话框 → Promise<boolean>
const ok = await MeToast.confirm('确定删除?', { confirmText: '删除', confirmColor: '#ef4444' });

// 输入对话框 → Promise<string|null>
const name = await MeToast.prompt('请输入姓名', { placeholder: '请输入...' });

// 进度条
const p = MeToast.progress('上传中...');
p.setProgress(60);
p.complete('上传完成!');

// 倒计时({seconds} 自动替换)
MeToast.countdown('操作将在 {seconds} 秒后执行', 5, {
  onComplete: () => MeToast.success('已执行'),
});

// 队列展示(顺序逐个)
await MeToast.queue(['步骤一', '步骤二', '步骤三'], { delay: 800 });

// 堆叠展示(同时错峰)
MeToast.stack(['消息1', '消息2', '消息3'], { stagger: 150 });

插件

// 安装预设插件
MeToast.use('keyboard');        // ESC 关闭所有 Toast
MeToast.use('persistence');     // 配置持久化到 localStorage
MeToast.use('accessibility');   // 屏幕阅读器朗读公告
MeToast.use('dedupe');          // 相同 type+message 自动去重(更新已有实例)

// 自定义插件(通过 install 中注册 Toast 生命周期钩子)
import MeToast, { Toast } from '@metona-team/metona-toast';

MeToast.use({
  name: 'my-plugin',
  install() {
    Toast.on('afterShow', (toast) => {
      console.log('Toast shown:', toast.message);
    });
  },
});

国际化

MeToast.i18n.switchLocale('en-US');

// 添加语言
MeToast.i18n.addTranslations('ja', {
  success: '成功',
  error: 'エラー',
  confirm: '確認',
  cancel: 'キャンセル',
});

// 格式化工具
MeToast.i18n.formatNumber(1234567);     // "1,234,567"
MeToast.i18n.formatCurrency(99, 'CNY'); // "¥99.00"

Toast 管理

MeToast.count();            // 当前数量
MeToast.getAll();           // 获取原生 Map<id, ToastInstance>
MeToast.getToasts();        // 获取所有实例数组
MeToast.dismiss();          // 关闭所有
MeToast.dismiss(id);        // 关闭指定
MeToast.pauseAll();         // 暂停所有计时
MeToast.resumeAll();        // 恢复所有计时
MeToast.destroy();          // 完全销毁(支持重复调用)

回调

MeToast.success({
  message: '操作成功',
  onShow: (toast) => console.log('显示:', toast.id),
  onClose: (toast) => console.log('关闭:', toast.id),
  onClick: (toast) => console.log('点击:', toast.id),
  onUpdate: (toast) => console.log('更新:', toast.id),
});

// 全局错误回调 — 捕获钩子异常和定时器错误
MeToast.configure({
  onError: ({ hook, source, error, toast }) => {
    // 接入监控系统 (Sentry, DataDog 等)
    reportError(error, { hook, source, toastId: toast?.id });
  },
});

React 适配器

主包保持零依赖;React 适配器通过子路径导入,react 为 optional peerDependency。

npm install @metona-team/metona-toast react
import { useToast, Toast } from '@metona-team/metona-toast/react';

function SubmitButton() {
  // 本组件创建的 Toast 会在组件卸载时自动移除
  const toast = useToast();

  const submit = async () => {
    const loading = toast.loading('正在提交...');
    try {
      await api.submit();
      loading.success('提交成功!');
    } catch (e) {
      loading.error('提交失败');
    }
  };

  return <button onClick={submit}>提交</button>;
}

// 声明式 Toast — props 变化时更新,组件卸载时自动移除
function SaveIndicator({ saving }: { saving: boolean }) {
  return saving ? <Toast type="info" message="正在保存..." /> : null;
}

License

MIT