@@ -157,7 +185,7 @@
-
🎭 11种动画效果
+
🎭 11 种动画效果
@@ -171,9 +199,14 @@
+
-
+
📍 全部位置
@@ -186,7 +219,7 @@
-
+
🎨 主题切换 & 自定义
@@ -194,24 +227,43 @@
-
+
-
+
-
🔌 插件系统
+
🔌 插件系统 4 款预设
-
-
-
+
+
+
+
+
-
+
-
🔥 高级特性 resetTimer · onUpdate · render · notify
+
🔗 钩子系统 beforeShow 拦截演示
+
+
+
+
+
+
+
+
钩子:未注册
+
+
+
+
+
🔥 高级特性
@@ -220,7 +272,7 @@
-
+
🔧 Toast 管理
@@ -230,7 +282,7 @@
-
+
@@ -238,32 +290,34 @@
+
-
+
-
🛡️ onError 错误捕获 v0.5.0
+
🛡️ onError 错误捕获
-
+
✋ 交互特性
-
-
-
-
+
+
+
+
+
-
+
🌍 国际化 & 格式化
@@ -273,16 +327,19 @@
-
+
-
🎨 107种预设图标 — 点击任意图标发送Toast
-
+
🎨 107 种预设图标 — 点击任意图标发送 Toast
+
@@ -290,27 +347,41 @@
MeToast.configure({ position:'top-right', duration:4000, theme:'dark', animation:'slide', pauseOnHover:true, showProgress:true });
// === Loading ===
- function demoLoading() { const l=MeToast.loading('正在提交...'); setTimeout(()=>l.success('提交成功!'),2000); }
- function demoLoadingFail() { const l=MeToast.loading('正在处理...'); setTimeout(()=>l.error('处理失败'),2000); }
- function demoLoadingWarn() { const l=MeToast.loading('检查中...'); setTimeout(()=>l.warning('发现潜在问题'),2000); }
- function demoLoadingUpdate() { const l=MeToast.loading('处理中 0%'); let i=1; const t=setInterval(()=>{if(i>3){clearInterval(t);l.success('完成!');return;} l.update({message:'处理中 '+i*33+'%'}); i++;},600); }
+ function demoLoading(mode) {
+ const l = MeToast.loading('正在处理...');
+ const showId = () => document.getElementById('loading-status').textContent = 'loading id: ' + l.id;
+ showId();
+ setTimeout(() => {
+ const map = { success:['提交成功!','success'], error:['处理失败','error'], warning:['发现潜在问题','warning'], update:null };
+ if (mode === 'update') {
+ l.update({ message: '进度 50%' });
+ setTimeout(() => l.success('完成!'), 1000);
+ return;
+ }
+ const [msg, type] = map[mode];
+ const t = l[type](msg);
+ if (t) document.getElementById('loading-status').textContent = '转换后 id: ' + t.id + '(' + (t.id === l.id ? '同一实例 ✓' : '不同实例 ✗') + ')';
+ }, 1600);
+ }
// === Promise / confirm / prompt ===
async function demoPromise() {
- const p=new Promise((r,e)=>setTimeout(()=>Math.random()>0.3?r('ok'):e('fail'),2000));
- try{await MeToast.promise(p,{loading:'处理中...',success:'成功!',error:'失败'});}catch(_){}
+ const p = new Promise((r,e)=>setTimeout(()=>Math.random()>0.3?r('ok'):e('fail'),2000));
+ try{ await MeToast.promise(p,{loading:'处理中...',success:'成功!',error:'失败'}); }catch(_){}
}
async function demoConfirm() { const ok=await MeToast.confirm('确定删除?',{confirmText:'删除',confirmColor:'#ef4444'}); if(ok)MeToast.success('已删除'); }
async function demoPrompt() { const v=await MeToast.prompt('请输入姓名',{placeholder:'请输入...'}); if(v) MeToast.info('你好,'+v+'!'); }
async function demoPromptPassword() { const v=await MeToast.prompt('请输入密码',{placeholder:'密码',inputType:'password',submitText:'登录'}); if(v) MeToast.info('密码已输入'); }
// === Progress & Countdown ===
- function demoProgress() { const p=MeToast.progress('上传中...',{progressColor:'#3b82f6'}); let v=0; const t=setInterval(()=>{v+=Math.random()*25; if(v>=100){clearInterval(t);p.complete('上传完成!');}else p.setProgress(v);},400); }
- function demoProgressV() { const p=MeToast.progress('上传中...',{progressColor:'#10b981',progressDirection:'vertical'}); let v=0; const t=setInterval(()=>{v+=Math.random()*25; if(v>=100){clearInterval(t);p.complete('完成!');}else p.setProgress(v);},400); }
+ function demoProgress(dir) {
+ const p=MeToast.progress('上传中...',{progressColor: dir==='vertical'?'#10b981':'#3b82f6', progressDirection: dir});
+ let v=0; const t=setInterval(()=>{v+=Math.random()*25; if(v>=100){clearInterval(t);p.complete('上传完成!');}else p.setProgress(v);},400);
+ }
function demoCountdown() { MeToast.countdown('{seconds} 秒后执行',5,{onComplete:()=>MeToast.success('已执行')}); }
function demoCountdownPause() {
const c=MeToast.countdown('{seconds}s 可暂停',10,{type:'info',onComplete:()=>MeToast.success('倒计时结束')});
- setTimeout(()=>{c.pause();MeToast.warning('已暂停3秒后恢复',{duration:3000})},4000);
+ setTimeout(()=>{c.pause();MeToast.warning('已暂停 3 秒后恢复',{duration:3000})},4000);
setTimeout(()=>{c.resume();MeToast.info('已恢复倒计时')},7000);
}
@@ -321,14 +392,15 @@
setTimeout(()=>{q.cancel();MeToast.warning('队列已取消',{duration:2000});},3500);
}
function demoStack() { MeToast.stack(['消息1','消息2','消息3','消息4','消息5'],{stagger:120,type:'info'}); }
- function demoStackMixed() {
- MeToast.stack(['普通消息',{message:'警告消息',type:'warning',duration:5000},{message:'错误消息',type:'error'}],{stagger:150});
- }
+ function demoStackMixed() { MeToast.stack(['普通消息',{message:'警告消息',type:'warning',duration:5000},{message:'错误消息',type:'error'}],{stagger:150}); }
// === Action ===
function demoActionDelete() { MeToast.action('文件 data.json 已删除',[{text:'撤销',onClick:()=>MeToast.success('已撤销'),color:'#3b82f6'}]); }
function demoActionMulti() { MeToast.action('邮件已发送',[{text:'查看',onClick:()=>MeToast.info('打开邮件详情'),color:'#3b82f6'},{text:'撤回',onClick:()=>MeToast.warning('已撤回'),color:'#f59e0b'}]); }
- function demoActionNoClose() { MeToast.action('完成后点击关闭',[{text:'我知道了',onClick:()=>{},color:'#10b981'}]); }
+ function demoActionNoClose() {
+ let n=0;
+ MeToast.action('点「+1」不会关闭,可连续点击',[{text:'+1',onClick:(t)=>{n++;t.update({message:'已点击 '+n+' 次(close:false 不关闭)'});},color:'#10b981',close:false}],{duration:8000});
+ }
// === Group ===
const group = MeToast.group('demo-group');
@@ -342,32 +414,71 @@
// === Animation ===
function anim(name) { MeToast.success('动画: '+name,{animation:name,duration:3000,showProgress:false}); }
+ function demoCustomAnim() {
+ MeToast.animations.register('swing', {
+ enter: { transform: 'rotate(-40deg) scale(0.3)', opacity: 0 },
+ leave: { transform: 'rotate(20deg) scale(0.5)', opacity: 0 },
+ duration: 600,
+ easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
+ });
+ MeToast.success('自定义 swing 动画!', { animation: 'swing', duration: 3000 });
+ }
// === Theme ===
function switchTheme(t) { MeToast.themes.switchTheme(t); MeToast.configure({theme:t}); MeToast.info('主题: '+t,{theme:t}); }
function demoRegisterTheme() {
try {
MeToast.themes.registerTheme('sunset',{bg:'rgba(255,248,240,0.96)',text:'#92400e',border:'rgba(249,115,22,0.2)',shadow:'0 10px 36px -10px rgba(249,115,22,0.2)',hoverShadow:'0 14px 48px -10px rgba(249,115,22,0.3)',progressBg:'rgba(249,115,22,0.1)',closeHoverBg:'rgba(249,115,22,0.1)'});
- MeToast.themes.switchTheme('sunset');
- MeToast.configure({theme:'sunset'});
+ MeToast.themes.switchTheme('sunset'); MeToast.configure({theme:'sunset'});
MeToast.success('已切换到 sunset 主题',{theme:'sunset'});
} catch(_) { MeToast.warning('主题已注册,已切换'); }
}
// === Plugins ===
function demoCustomPlugin() {
- MeToast.use({name:'click-counter',version:'1.0',install(){let c=0;Toast.on('afterShow',t=>{c++;setTimeout(()=>t.update({message:t.message.replace(/\\(\\d+\\)/,'')+' (总通知#'+c+')'}),1);});}});
+ MeToast.use({name:'click-counter',version:'1.0',install(){let c=0;Toast.on('afterShow',t=>{c++;setTimeout(()=>t.update({message:t.message.replace(/\(\d+\)/,'')+' (总通知#'+c+')'}),1);});}});
MeToast.info('自定义插件已安装(消息后追加计数)');
}
+ function demoDedupe() {
+ if (!MeToast.plugins.has('dedupe')) MeToast.use('dedupe');
+ MeToast.info('同一条消息');
+ MeToast.info('同一条消息');
+ MeToast.info('同一条消息');
+ MeToast.info('当前仅 ' + MeToast.count() + ' 条 — 相同消息自动合并');
+ }
+
+ // === Hooks ===
+ let hookOff = null;
+ function demoHookBlock() {
+ if (hookOff) hookOff();
+ hookOff = Toast.on('beforeShow', (toast) => {
+ if (toast.type === 'error') { document.getElementById('hook-status').textContent = '钩子:error 已被拦截 ✋'; return false; }
+ });
+ document.getElementById('hook-status').textContent = '钩子:已注册(error 类型将被拦截)';
+ MeToast.info('拦截器已开启 — 试试触发 error');
+ }
+ function demoHookUnblock() {
+ if (hookOff) { hookOff(); hookOff = null; }
+ document.getElementById('hook-status').textContent = '钩子:已移除';
+ MeToast.info('拦截器已关闭');
+ }
+ function demoHookCount() {
+ const stats = [];
+ ['afterShow','afterClose','configChange','themeChange'].forEach(name => {
+ const list = Toast._hooks.get(name);
+ stats.push(name + ':' + (list ? list.length : 0));
+ });
+ MeToast.info('钩子数量 — ' + stats.join(' '), { duration: 5000 });
+ }
// === 高级特性 ===
function demoResetTimer() {
- const t=MeToast.info('这条消息不会自动消失(resetTimer)',{duration:3000,resetTimerOnUpdate:true,closeButton:true});
- let n=0; const iv=setInterval(()=>{n++;if(n>8){clearInterval(iv);t.close();return;}t.update({message:'重置计时器 #'+n+' — 每次更新重置3秒'});},1000);
+ const t=MeToast.info('这条不会自动消失(每次更新重置 3 秒)',{duration:3000,resetTimerOnUpdate:true,closeButton:true});
+ let n=0; const iv=setInterval(()=>{n++;if(n>8){clearInterval(iv);t.close();return;}t.update({message:'重置计时器 #'+n});},1000);
}
- function demoOnUpdate() { const t=MeToast.success('onUpdate 演示',{onUpdate:t=>{console.log('更新了:',t.message);}}); setTimeout(()=>t.update({message:'已更新!'}),1000); setTimeout(()=>t.update({message:'再次更新!'}),2000); }
+ function demoOnUpdate() { const t=MeToast.success('onUpdate 演示',{onUpdate:t=>{console.log('更新了:',t.message);}}); setTimeout(()=>t.update({message:'已更新!'}),1000); }
function demoCustomRender() {
- MeToast.show({render:toast=>'
🟢 '+toast.message+'
# custom render by thzxx
',message:'自定义渲染内容',duration:5000});
+ MeToast.show({render:toast=>'
🟢 '+toast.message+'
# custom render
',message:'自定义渲染内容',duration:5000});
}
function demoNotify() {
if(typeof Notification==='undefined'){MeToast.error('浏览器不支持 Notification');return;}
@@ -382,48 +493,38 @@
}
function demoGetAll() { const all=MeToast.getAll(); MeToast.info('当前共 '+all.size+' 条 toast'); }
function demoClearByPos() { MeToast.error('右下角临时消息',{position:'bottom-right',duration:3000}); setTimeout(()=>{MeToast.clear('bottom-right');MeToast.info('右下角已清除');},500); }
- function demoGetConfig() { try{const c=MeToast.getConfig();MeToast.info('配置: '+JSON.stringify({position:c.position,duration:c.duration,theme:c.theme,animation:c.animation,locale:c.locale}).replace(/"/g,''),{duration:5000});}catch(e){MeToast.error('无 getConfig 方法');} }
-
- // === Interaction ===
- function demoDraggable() { MeToast.info('← 试试拖动我来关闭',{draggable:true,duration:8000,closeButton:true}); }
- function demoPauseHover() { MeToast.info('鼠标悬停在我上面,倒计时会暂停',{pauseOnHover:true,duration:8000}); }
- function demoCloseOnClick() { MeToast.warning('点击我即可关闭',{closeOnClick:true,duration:10000}); }
- function demoWidth() { MeToast.info('宽550px',{width:550}); setTimeout(()=>MeToast.info('宽280px(窄)',{width:280}),500); }
- function demoClassName() { MeToast.success('带自定义 className',{className:'demo-custom-class'}); }
- function demoCustomIcon() {
- MeToast.info('通用通知', {
- iconHTML: '
'
- });
- }
+ function demoGetConfig() { const c=MeToast.getConfig(); MeToast.info('配置: '+JSON.stringify({position:c.position,duration:c.duration,theme:c.theme,animation:c.animation,locale:c.locale}).replace(/"/g,''),{duration:5000}); }
+ function demoRemove() { const t=MeToast.warning('这条将被立即移除(无动画)'); setTimeout(()=>{MeToast.removeToast(t.id);MeToast.info('已立即移除');},800); }
// === onError ===
function demoOnError() {
- MeToast.configure({
- onError: function(info) {
- MeToast.error('⚠️ 捕获错误: ' + (info.hook || info.source), { duration: 3000, position: 'bottom-center' });
- }
- });
- MeToast.info('已启用 onError 监控(钩子/定时器异常将被捕获)', { duration: 3000 });
- // 触发一个钩子错误来演示
+ MeToast.configure({ onError: function(info) { MeToast.error('⚠️ 捕获: ' + (info.hook || info.source), { duration: 3000, position: 'bottom-center' }); } });
+ MeToast.info('已启用 onError 监控', { duration: 3000 });
var badFn = function() { throw new Error('demo-error'); };
Toast.on('afterShow', badFn);
- MeToast.success('这条消息会触发错误', { duration: 2000 });
+ MeToast.success('这条会触发错误回调', { duration: 2000 });
setTimeout(function() { Toast.off('afterShow', badFn); }, 3000);
}
- function demoOnErrorReset() {
- MeToast.configure({ onError: null });
- MeToast.info('onError 已重置为 null');
+ function demoOnErrorReset() { MeToast.configure({ onError: null }); MeToast.info('onError 已重置'); }
+
+ // === Interaction ===
+ function demoDraggable() { MeToast.info('← 拖动我超过 120px 关闭',{draggable:true,duration:8000,closeButton:true}); }
+ function demoDragThreshold() { MeToast.info('← 拖动阈值 300px(要拖更远)',{draggable:true,dragThreshold:300,duration:8000,closeButton:true}); }
+ function demoPauseHover() { MeToast.info('悬停时倒计时会暂停',{pauseOnHover:true,duration:8000}); }
+ function demoCloseOnClick() { MeToast.warning('点击我即可关闭',{closeOnClick:true,duration:10000}); }
+ function demoWidth() { MeToast.info('宽 550px',{width:550}); setTimeout(()=>MeToast.info('宽 280px(窄)',{width:280}),500); }
+ function demoClassName() { MeToast.success('带自定义 className',{className:'demo-custom-class'}); }
+ function demoCustomIcon() {
+ MeToast.info('通用通知', { iconHTML: '
' });
}
// === i18n ===
function switchLocale(loc) {
- try{MeToast.i18n.switchLocale(loc);MeToast.configure({locale:loc});MeToast.success(loc==='zh-CN'?'已切换到中文':'Switched to English',{theme:'dark'});}
- catch(e){MeToast.info('已切换语言');}
+ MeToast.i18n.switchLocale(loc); MeToast.configure({locale:loc});
+ MeToast.success(loc==='zh-CN'?'已切换到中文':'Switched to English');
}
function demoFormat() {
- try{
- MeToast.info('数字: '+MeToast.i18n.formatNumber(1234567)+' | 货币: '+MeToast.i18n.formatCurrency(99.5,'CNY')+' | 日期: '+MeToast.i18n.formatDate(new Date()),{duration:8000});
- }catch(e){MeToast.info('格式化: 1234567 → 1,234,567');}
+ MeToast.info('数字: '+MeToast.i18n.formatNumber(1234567)+' | 货币: '+MeToast.i18n.formatCurrency(99.5,'CNY')+' | 日期: '+MeToast.i18n.formatDate(new Date()),{duration:8000});
}
// === 图标网格 ===
diff --git a/site/docs.html b/site/docs.html
index df53864..000e275 100644
--- a/site/docs.html
+++ b/site/docs.html
@@ -7,499 +7,442 @@
API 文档 — MetonaToast
API 文档
-MetonaToast v0.5.0 完整 API 参考。所有基础通知方法(show/success/error/warning/info/loading)支持两种调用形式,均可传入任何 配置项 作为可选第二参数。
+MetonaToast v0.5.0 完整 API 参考。所有基础通知方法支持字符串与对象两种调用形式,均可传入任何配置项作为可选参数。
show(message, opts?)
-显示默认类型的 Toast 通知,无特定颜色和图标。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 消息字符串,或包含 title/message 等属性的配置对象 |
-| opts | object | {} | 可选配置对象,覆盖 全部配置项。仅当 message 为字符串时有效 |
-
+显示默认类型 Toast,无特定颜色与图标。
+show.js
// 字符串形式
MeToast.show('默认消息');
-MeToast.show('自定义', { duration: 2000, position: 'bottom-center' });
-// 对象形式(所有 opts 作为一级属性)
-MeToast.show({ title: '标题', message: '内容', duration: 3000 });
+MeToast.
show(
'自定义', {
duration:
2000,
position:
'bottom-center' });
+
// 对象形式
+MeToast.
show({
title:
'标题',
message:
'内容',
duration:
3000 });
success(message, opts?)
-显示成功通知。绿色对勾图标 #10b981,type = success。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 消息字符串或配置对象 |
-| opts | object | {} | 可选配置覆盖。type 固定为 success |
-
+绿色对勾图标 #10b981,type = success。
+success.js
MeToast.success('保存成功!');
MeToast.success({ title: '已保存', message: '数据已同步' });
-Met.success('MeToast和Met等价');
+Met.
success(
'Met 与 MeToast 等价');
error(message, opts?)
-显示错误通知。红色叉号图标 #ef4444,type = error。aria-live 设为 assertive。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 消息字符串或配置对象 |
-| opts | object | {} | 可选配置覆盖。type 固定为 error |
-
+红色叉号图标 #ef4444,type = error。aria-live = assertive。
+error.js
MeToast.error('网络错误,请重试');
-MeToast.error({ title: '提交失败', message: '服务器不可达' });
+MeToast.
error({
title:
'提交失败',
message:
'服务器不可达' });
warning(message, opts?)
-显示警告通知。黄色三角图标 #f59e0b,type = warning。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 消息字符串或配置对象 |
-| opts | object | {} | 可选配置覆盖。type 固定为 warning |
-
-MeToast.warning('请注意检查输入内容');
+黄色三角图标 #f59e0b,type = warning。
+warning.js
+
MeToast.warning('请注意检查输入内容');
info(message, opts?)
-显示信息通知。蓝色圆形图标 #3b82f6,type = info。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 消息字符串或配置对象 |
-| opts | object | {} | 可选配置覆盖。type 固定为 info |
-
-MeToast.info('系统将于 22:00 维护');
+蓝色圆形图标 #3b82f6,type = info。
+info.js
+
MeToast.info('系统将于 22:00 维护');
loading(message, opts?)
-显示加载状态。type = loading,duration 强制为 0(不自动关闭),closeButton 和 showProgress 强制为 false。返回 LoadingControl 对象。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 加载提示文本或配置对象 |
-| opts | object | {} | 可选配置覆盖 |
-
+type = loading,duration 强制 0(不自动关闭)。返回 LoadingControl。
+loading.js
const loading = MeToast.loading('正在提交...');
-setTimeout(() => loading.success('提交成功!'), 2000);
+
setTimeout(() => loading.
success(
'提交成功!'),
2000);
-
返回 LoadingControl
+
LoadingControl(链式转换 id 稳定)
-| 方法 | 签名 | 说明 |
-| success | (msg, opts?) => ToastInstance | 关闭加载 toast,原地替换为 success 类型 |
-| error | (msg, opts?) => ToastInstance | 替换为 error 类型 |
-| info | (msg, opts?) => ToastInstance | 替换为 info 类型 |
-| warning | (msg, opts?) => ToastInstance | 替换为 warning 类型 |
-| update | (partial) => MeToast | 不关闭加载 toast,原地更新其内容(支持 resetTimerOnUpdate) |
-| dismiss | () | 直接关闭加载 toast 不替换 |
+| 方法 | 说明 |
+| success/error/info/warning | 原地更新为对应类型(同一实例,id 不变),duration 自动恢复默认值 |
+| update(partial) | 原地更新内容,不改变类型 |
+| dismiss() | 直接关闭,不转换 |
promise(promise, opts)
-监听 Promise 生命周期。自动显示 loading → 根据 resolve/reject 自动切换 success/error。返回原 Promise,支持 await 获取结果。
+监听 Promise 生命周期,自动 loading → resolve/reject 切换 success/error,返回原 Promise。
-| 参数 | 类型 | 默认值 | 说明 |
-| promise | Promise | — | 要监听的 Promise 对象。非 Promise 会打印错误并返回 rejected Promise |
-| opts.loading | string | "加载中..." | 加载中显示的文本 |
-| opts.success | string | "操作成功" | resolve 后显示的文本 |
-| opts.error | string | "操作失败" | reject 后显示的文本 |
+| 参数 | 类型 | 默认 | 说明 |
+| promise | Promise | — | 要监听的 Promise。非 Promise 打印错误并返回 rejected Promise |
+| opts.loading / success / error | string | 加载中... / 操作成功 / 操作失败 | 各阶段文本 |
+promise.js
try {
await MeToast.promise(fetch('/api/data'), {
- loading: '加载中...',
- success: '加载完成!',
- error: '加载失败',
+ loading: '加载中...', success: '完成!', error: '失败',
});
-} catch (e) { /* promise reject 会继续抛出 */ }
+}
catch (e) {
/* reject 会继续抛出 */ }
confirm(message, opts?)
-确认对话框。返回 Promise<boolean>。内置 10 秒安全超时,超时自动 resolve(false)。
+确认对话框,返回 Promise<boolean>,内置 10 秒安全超时自动 resolve(false)。
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | — | 对话框消息。非字符串会打印错误并 resolve(false) |
-| opts.confirmText | string | "确认" | 确认按钮文字 |
-| opts.confirmColor | string | "#10b981" | 确认按钮背景色 |
-| opts.cancelText | string | "取消" | 取消按钮文字 |
-| opts.cancelColor | string | "#6b7280" | 取消按钮背景色 |
-| opts.type | string | "warning" | Toast 类型(影响图标和颜色) |
+| 参数 | 默认 | 说明 |
+| confirmText / confirmColor | 确认 / #10b981 | 确认按钮文字与颜色 |
+| cancelText / cancelColor | 取消 / #6b7280 | 取消按钮文字与颜色 |
+| type | warning | Toast 类型 |
-const ok = await MeToast.confirm('确定删除?', {
- confirmText: '删除',
- confirmColor: '#ef4444',
- cancelText: '保留',
-});
-if (ok) MeToast.success('已删除');
+confirm.js
+
const ok = await MeToast.confirm('确定删除?', { confirmText: '删除', confirmColor: '#ef4444' });
+if (ok) MeToast.success('已删除');
prompt(message, opts?)
-输入对话框。返回 Promise<string|null>。按 Enter 或点击提交按钮返回输入值,取消返回 null。内置 10 秒安全超时。
+输入对话框,返回 Promise<string|null>。Enter / 提交返回输入值,取消返回 null,10 秒超时。
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | — | 对话框消息。非字符串会打印错误并 resolve(null) |
-| opts.placeholder | string | "" | 输入框占位文字 |
-| opts.defaultValue | string | "" | 输入框默认值 |
-| opts.inputType | string | "text" | input 标签 type 属性(如 password/email/number) |
-| opts.submitText | string | "确认" | 提交按钮文字 |
-| opts.submitColor | string | "#3b82f6" | 提交按钮背景色 |
-| opts.cancelText | string | "取消" | 取消按钮文字 |
-| opts.cancelColor | string | "#6b7280" | 取消按钮背景色 |
-| opts.type | string | "info" | Toast 类型 |
+| 参数 | 默认 | 说明 |
+| placeholder / defaultValue | "" | 输入框占位与默认值 |
+| inputType | text | input 的 type(password/email/number 等) |
+| submitText / submitColor | 确认 / #3b82f6 | 提交按钮 |
+| cancelText / cancelColor | 取消 / #6b7280 | 取消按钮 |
-const name = await MeToast.prompt('请输入姓名', {
- placeholder: '请输入...',
- defaultValue: '张三',
- submitText: '确定',
-});
-if (name) MeToast.info('你好,' + name);
+prompt.js
+
const name = await MeToast.prompt('请输入姓名', { placeholder: '请输入...' });
+if (name) MeToast.info('你好,' + name);
progress(message, opts?)
-进度条通知。不自动关闭。返回 ProgressControl 对象以手动更新进度。
+进度条通知,不自动关闭。返回 ProgressControl。
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 进度提示文本或配置对象 |
-| opts | object | {} | 可选配置。type 默认 info |
-| opts.progressColor | string | "#3b82f6" | 进度条填充颜色 |
+| 方法 | 说明 |
+| setProgress(percent) | 设置进度 0~100,自动 clamp |
+| complete(message?) | 跳 100%,300ms 后转 success,1s 后自动关闭 |
+| error(message?) | 转 error,2s 后自动关闭 |
+| dismiss() | 直接关闭 |
+progress.js
const p = MeToast.progress('上传中...', { progressColor: '#10b981' });
-p.setProgress(45); // → 45%
-p.setProgress(90); // → 90%
-p.complete('上传完成!'); // → 100% → success
-// 或
-p.error('上传失败');
-p.dismiss();
-
-
返回 ProgressControl
-
-| 方法 | 签名 | 说明 |
-| setProgress | (percent: number) | 设置进度 0~100,自动 clamp。更新进度条宽度和百分比文字 |
-| complete | (message?: string) | 跳到 100%,300ms 后替换为 success toast,1s 后自动关闭 |
-| error | (message?: string) | 替换为 error toast,2s 后自动关闭 |
-| dismiss | () | 直接关闭 |
-
-
+p.
setProgress(
45);
+p.
complete(
'上传完成!');
countdown(message, seconds, opts?)
-倒计时 Toast。{seconds} 占位符每秒自动替换为剩余秒数。
+倒计时 Toast,{seconds} 占位符每秒自动替换。
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | — | 消息文本。支持 {seconds} 占位符。非字符串打印错误并返回空控制对象 |
-| seconds | number | 10 | 倒计时秒数,最小 1 |
-| opts.onComplete | function | — | 倒计时归零时的回调 |
-| opts.type | string | "warning" | Toast 类型 |
+| 参数 | 默认 | 说明 |
+| seconds | 10 | 倒计时秒数,最小 1 |
+| opts.onComplete | — | 归零回调 |
-MeToast.countdown('{seconds} 秒后执行', 5, {
- onComplete: () => MeToast.success('已执行'),
-});
-
-
返回 CountdownControl
-
-| 方法 | 说明 |
-| cancel() | 清除计时器并关闭 toast |
-| pause() | 暂停倒计时 |
-| resume() | 恢复倒计时 |
-
-
+countdown.js
+
MeToast.countdown('{seconds} 秒后执行', 5, { onComplete: () => MeToast.success('已执行') });
+// 返回 { cancel(), pause(), resume() }
queue(messages, opts?)
-顺序逐个显示消息队列。前一条关闭后延时显示下一条。返回 QueueControl(thenable + cancel)。
+顺序逐个显示消息队列,返回 thenable + cancel。支持 await。
-| 参数 | 类型 | 默认值 | 说明 |
-| messages | Array<string|object> | — | 消息数组。可为字符串或带 message/type/duration/onClose 的对象。非数组打印错误并返回空控制对象 |
-| opts.delay | number | 1000 | 每条消息关闭后到显示下一条的间隔(ms) |
-| opts.duration | number | 3000 | 每条消息显示时长(ms),可被消息级 duration 覆盖 |
-| opts.onClose | function | — | 全部队列完成后的回调。消息级 onClose 和队列级 onClose 都会依次调用 |
-| opts.type | string | — | 默认类型 |
+| 参数 | 默认 | 说明 |
+| opts.delay | 1000 | 每条关闭后到下一条的间隔(ms) |
+| opts.duration | 3000 | 每条显示时长,可被消息级 duration 覆盖 |
-const q = MeToast.queue([
- '步骤一',
- { message: '步骤二', duration: 5000, type: 'warning' },
- '步骤三',
-], { delay: 800, duration: 2000, type: 'info' });
-q.cancel(); // 中途取消
-await q; // 等待完成(thenable 支持 await)
-
-
返回 QueueControl (thenable)
-
-| 方法 | 说明 |
-| .then(fn, rj) | Promise.then 代理,支持 await |
-| .catch(rj) | Promise.catch 代理 |
-| .cancel() | 设置取消标志,不再显示下一条消息 |
-
-
+queue.js
+
const q = MeToast.queue(['步骤一', '步骤二', '步骤三'], { delay: 800, duration: 2000 });
+q.cancel(); // 中途取消
+await q; // 等待完成
stack(messages, opts?)
-同时错峰显示多条消息。每条间隔 stagger 毫秒依次出现,全部叠加在屏幕上。
-
-| 参数 | 类型 | 默认值 | 说明 |
-| messages | Array<string|object> | — | 消息数组。可为字符串或带 message/type/duration 的对象。非数组打印错误 |
-| opts.stagger | number | 100 | 每条消息之间的显示间隔(ms) |
-| opts.type | string | — | 默认类型 |
-
-MeToast.stack(['消息1', '消息2', { message: '警告', type: 'warning' }], {
- stagger: 150,
- type: 'info',
-});
+同时错峰显示多条消息。
+stack.js
+
MeToast.stack(['消息1', '消息2', { message: '警告', type: 'warning' }], { stagger: 150 });
action(message, actions, opts?)
-Action Toast:内嵌操作按钮。默认 duration=0 不自动关闭,closeButton=true。
+内嵌操作按钮。默认 duration=0 不自动关闭。点击按钮不会误触 closeOnClick(已隔离冒泡)。
-| 参数 | 类型 | 默认值 | 说明 |
-| message | string | object | — | 消息字符串或配置对象 |
-| actions | ActionButton[] | [] | 按钮数组。每个按钮可配 text/onClick/color/style/close |
-| opts | object | {} | 可选配置。duration 默认 0,closeButton 默认 true |
-
-
-| ActionButton 字段 | 类型 | 默认值 | 说明 |
-| text | string | —(必填) | 按钮显示文字 |
-| onClick | (toast) => void | —(必填) | 点击回调函数,参数为当前 toast 实例 |
-| color | string | "#6366f1" | 按钮背景色 |
-| style | object | {} | 按钮内联样式,可覆盖 color(style.background 优先) |
-| close | boolean | true | 点击后是否自动关闭 toast。设为 false 可多次点击 |
+| ActionButton | 默认 | 说明 |
+| text | —(必填) | 按钮文字 |
+| onClick | —(必填) | 点击回调,参数为当前 toast |
+| color / style | #6366f1 | 背景色 / 内联样式(style.background 优先) |
+| close | true | 点击后是否关闭。false 可多次点击 |
+action.js
MeToast.action('文件已删除', [
{ text: '撤销', onClick: () => restore(), color: '#3b82f6' },
- { text: '查看详情', onClick: (t) => openFile(), color: '#10b981', close: false },
-]);
+ {
text:
'查看',
onClick: () =>
open(),
color:
'#10b981',
close:
false },
+]);
group(name)
-创建 Toast 分组,返回 GroupAPI 对象。该对象所有方法自动传入 group: name,按组管理。
-
-| 参数 | 类型 | 说明 |
-| name | string | 分组名称。GroupAPI 和 dismissGroup 通过此名称关联 |
-
+创建分组,返回 GroupAPI。所有方法自动注入 group: name。
+group.js
const orders = MeToast.group('orders');
orders.success('订单已创建');
-orders.error('支付失败', { duration: 5000 });
-orders.count(); // 该组当前 toast 数量
-orders.dismiss(); // 关闭该组全部 toast
-// 也支持主对象关闭
-MeToast.dismissGroup('orders');
-
-
返回 GroupAPI
-
-| 方法 | 说明 |
-| show(msg, opts?) | 等价 MeToast.show,自动注入 group |
-| success(msg, opts?) | 等价 MeToast.success |
-| error(msg, opts?) | 等价 MeToast.error |
-| warning(msg, opts?) | 等价 MeToast.warning |
-| info(msg, opts?) | 等价 MeToast.info |
-| loading(msg, opts?) | 等价 MeToast.loading |
-| action(msg, actions, opts?) | 等价 MeToast.action |
-| dismiss() | 关闭该组所有 toast |
-| count() | 返回该组当前 toast 数量 |
-
-
+orders.
count();
// 该组数量
+orders.
dismiss();
// 关闭整组
+MeToast.
dismissGroup(
'orders');
dismiss(id?)
-关闭 Toast。无参数则关闭全部。
-
-| 参数 | 类型 | 说明 |
-| id | string | 可选。Toast 的 id,不传则关闭所有 |
-
-MeToast.dismiss(); // 关闭所有
-MeToast.dismiss(toast.id); // 关闭指定
+关闭 Toast。无参数关闭全部,传入 id 关闭指定。
+dismiss.js
+
MeToast.dismiss(); // 全部
+MeToast.dismiss(toast.id); // 指定
clear(position?)
-按位置清除 Toast。不传参数清除所有位置。
-
-| 参数 | 类型 | 说明 |
-| position | string | 可选。位置如 'top-right',不传则清除全部 |
-
-MeToast.clear(); // 全部
-MeToast.clear('bottom-right'); // 仅右下角
+按位置清除。
+clear.js
+
MeToast.clear(); // 全部
+MeToast.clear('bottom-right'); // 仅右下角
-updatePosition(position)
-运行时将 Toast 移动到新的位置容器。立即生效,不重新播放入场动画。
-
-| 参数 | 类型 | 说明 |
-| position | string | 目标位置:top-left / top-center / top-right / bottom-left / bottom-center / bottom-right |
-
+updatePosition(position) v0.3
+运行时将 Toast 移动到新位置容器,立即生效。
+position.js
const t = MeToast.info('可移动的 Toast');
-t.updatePosition('bottom-left'); // 移动到左下角
+t.
updatePosition(
'bottom-left');
-remove() / removeToast(id)
-立即从 DOM 和内存中移除 Toast,不触发离场动画。适用于需要无动画快速清除的场景(区别于 dismiss() 的优雅关闭)。
-
-| 方法 | 签名 | 说明 |
-| remove | toast.remove() | Toast 实例方法,立即移除自身 |
-| removeToast | MeToast.removeToast(id) | 按 id 立即移除。id 不存在时静默忽略 |
-
-const t = MeToast.warning('临时消息');
-t.remove(); // 实例方法:立即移除
-MeToast.removeToast(t.id); // 或按 id 移除(等价)
+remove() / removeToast(id) 立即移除
+立即从 DOM 和内存移除,不触发离场动画。适用于无动画快速清除。
+remove.js
+
t.remove(); // 实例方法
+MeToast.removeToast(t.id); // 按 id(等价)
-全局配置,影响后续所有 Toast。theme 和 locale 变化会触发相应副作用(应用主题 CSS / 切换语言)。
-
-| 参数 | 类型 | 说明 |
-| opts | object | 包含任意 配置项 的对象 |
-
+全局配置,影响后续所有 Toast。theme/locale 变化触发副作用。
+configure.js
MeToast.configure({
- position: 'top-right',
- duration: 4000,
- theme: 'dark',
- animation: 'slide',
- locale: 'zh-CN',
-});
+
position:
'top-right',
duration:
4000,
theme:
'dark',
+
animation:
'slide',
locale:
'zh-CN',
+});
use(plugin)
-安装插件。支持字符串(内置预设名)或插件对象。已被拒绝注册的无效插件会打印错误。
-
-| 参数 | 类型 | 说明 |
-| plugin | string | object | 预设名 'keyboard'/'persistence'/'accessibility' 或自定义插件对象 { name, version?, install, uninstall? } |
-
-MeToast.use('keyboard'); // ESC 关闭所有 Toast
-MeToast.use('persistence'); // 配置自动保存到 localStorage
-MeToast.use('accessibility'); // 屏幕阅读器实时朗读 toast 内容
-MeToast.use('dedupe'); // 相同 type+message 自动去重
-内置插件详情:keyboard 监听 keydown Escape 键;persistence 将配置写入 localStorage 并在页面加载时恢复;accessibility 在 afterShow/afterUpdate 钩子中通过 aria-live 区域朗读 toast 内容;dedupe 在 beforeShow 钩子中检测相同 type+message 的已有 toast,更新它并阻止重复弹出。
+安装插件:字符串预设名或插件对象。
+use.js
+
MeToast.use('keyboard'); // ESC 关闭所有
+MeToast.use('persistence'); // 配置持久化 localStorage
+MeToast.use('accessibility'); // 屏幕阅读器朗读
+MeToast.use('dedupe'); // 相同 type+message 自动去重
+dedupe:检测已有相同 toast,更新它并阻止重复弹出(beforeShow 钩子实现,支持 uninstall 卸载钩子)。
destroy()
-完全销毁。关闭所有 Toast、移除所有 DOM 容器和注入的样式标签、清空内存缓存。
-MeToast.destroy();
+完全销毁:关闭所有 Toast、移除全部 DOM 容器与注入样式、清空内存缓存与钩子。支持重复调用,init() 可恢复。
+destroy.js
+
MeToast.destroy();
+MeToast.init({ config: { duration: 3000 } }); // 恢复
-
-React 适配器
-主包保持零依赖。React 适配器通过子路径 @metona-team/metona-toast/react 导入,react 为 optional peerDependency,未使用 React 的项目不受影响。
-// 安装
-npm install @metona-team/metona-toast react
+
+钩子系统 v0.3
+Toast.on(name, handler) 注册钩子并返回取消函数。beforeShow / beforeClose / beforeUpdate 的 handler 返回 false 可拦截对应操作。
+
+| 钩子 | 触发时机 | 拦截 |
+| beforeInit / afterInit | init() 前后 | — |
+| beforeDestroy / afterDestroy | destroy() 前后 | — |
+| beforeShow / afterShow | 显示前后 | ✅ 返回 false 阻止 |
+| beforeClose / afterClose | 关闭前后 | ✅ 返回 false 阻止 |
+| beforeUpdate / afterUpdate | update() 前后 | ✅ 返回 false 阻止 |
+| configChange | configure / updateConfig / resetConfig | — |
+| themeChange / localeChange | 主题 / 语言切换 | — |
+| click / hover | 点击 / 悬停进出 | — |
+| dragStart / dragEnd | 拖拽开始 / 结束 | — |
+| animationStart / animationEnd | 入场动画开始 / 结束 | — |
+| progressStart / progressEnd | 倒计时开始 / 归零 | — |
+
+hooks.js
+
import MeToast, { Toast } from '@metona-team/metona-toast';
-// useToast — 组件卸载时自动移除本组件创建的 Toast
-import { useToast, Toast } from '@metona-team/metona-toast/react';
+// 拦截:非允许时段禁止错误提示
+const off = Toast.on('beforeShow', (toast) => {
+ if (toast.type === 'error' && !isAllowed) return false;
+});
+// ...
+off(); // 取消注册
+
+
+React 适配器 v0.4
+主包保持零依赖。通过子路径 @metona-team/metona-toast/react 导入,react 为 optional peerDependency。
+react.tsx
+
import { useToast, Toast } from '@metona-team/metona-toast/react';
function SubmitButton() {
- const toast = useToast();
+ const toast = useToast(); // 组件卸载自动清理本组件创建的 Toast
const submit = async () => {
const loading = toast.loading('正在提交...');
- try { await api(); loading.success('提交成功!'); }
- catch (e) { loading.error('提交失败'); }
+ try { await api(); loading.success('完成!'); }
+ catch (e) { loading.error('失败'); }
};
return <button onClick={submit}>提交</button>;
}
-// Toast 组件 — props 变化时更新,卸载时自动移除(autoClose 默认 true)
+// 声明式 Toast:props 变化更新,卸载自动移除(autoClose 默认 true)
function SaveIndicator({ saving }) {
return saving ? <Toast type="info" message="正在保存..." /> : null;
-}
+}
全部配置项
-以下配置可用于 configure()、init() 或单个 Toast 方法的 opts 参数。
+可用于 configure()、init() 或单个 Toast 方法的 opts。
| 配置项 | 类型 | 默认值 | 说明 |
-| position | string | 'top-right' | 位置:top-left / top-center / top-right / bottom-left / bottom-center / bottom-right |
-| duration | number | 4000 | 显示时长(ms),0=不自动关闭 |
-| max | number | 6 | 同一位置最多同时显示条数,超出则关闭最早的 |
-| gap | number | 12 | Toast 之间的间距(px) |
-| offset | number | 24 | 容器到屏幕边缘的距离(px) |
-| pauseOnHover | boolean | true | 鼠标悬停时暂停 duration 倒计时和进度条 |
-| closeOnClick | boolean | true | 点击 Toast 任意位置关闭。关闭按钮始终触发关闭 |
-| draggable | boolean | true | 允许拖拽关闭。拖拽超过 dragThreshold 触发关闭 |
-| dragThreshold | number | 120 | 拖拽关闭阈值(px),位移超过该值松手即关闭 |
-| showProgress | boolean | true | 显示 duration 倒计时进度条 |
-| progressDirection | string | 'horizontal' | 进度条方向:horizontal(底部水平) / vertical(右侧垂直) |
-| icon | boolean | true | 显示类型对应图标(success→对勾等) |
-| closeButton | boolean | true | 显示右上角关闭 × 按钮 |
-| theme | string | 'auto' | 主题:light / dark / auto(跟随系统)/ warm / 自定义注册名 |
-| animation | string | 'slide' | 入场动画名称,支持 CSS 动画列表见下 |
-| zIndex | number | 9999 | 容器 CSS z-index |
-| width | number|string | 360 | Toast 宽度。数字表示 px |
-| className | string | '' | 附加到 Toast 元素上的 CSS 类名 |
-| style | object | {} | 附加到 Toast 元素上的内联样式对象 |
-| locale | string | 'zh-CN' | 语言代码(zh-CN / en-US 等) |
- | resetTimerOnUpdate | boolean | false | 调用 update() 时重置 duration 倒计时 |
- | notifyWhenHidden | boolean | false | 页面不可见时自动通过 Notification API 发送系统通知 |
- | render | function | — | 自定义渲染函数 (toast) => htmlString,完全接管 DOM 构建 |
- | onBeforeShow | function | — | 显示前回调 (toast) => boolean | void,返回 false 可阻止该 Toast 显示 |
- | onError | function | — | 全局错误回调 ({ hook, source, error, toast }) => void,钩子异常或定时器错误时触发 |
+| position | string | 'top-right' | 6 个位置之一(RTL 自动翻转) |
+| duration | number | 4000 | 显示时长(ms),0 = 不自动关闭 |
+| max | number | 6 | 同位置最多条数,超出真正关闭最早的 |
+| gap | number | 12 | Toast 间距(px) |
+| offset | number | 24 | 容器距屏幕边缘(px) |
+| pauseOnHover | boolean | true | 悬停暂停倒计时 |
+| closeOnClick | boolean | true | 点击关闭 |
+| draggable | boolean | true | 允许拖拽关闭 |
+| dragThreshold | number | 120 | 拖拽关闭阈值(px) |
+| showProgress | boolean | true | 显示倒计时进度条 |
+| progressDirection | string | 'horizontal' | horizontal / vertical |
+| icon | boolean | true | 显示类型图标 |
+| closeButton | boolean | true | 显示关闭 × 按钮 |
+| theme | string | 'auto' | light / dark / auto / warm / 自定义名 |
+| animation | string | 'slide' | 11 种内置或自定义动画名 |
+| zIndex | number | 9999 | 容器 z-index |
+| width | number|string | 360 | 宽度,数字表示 px |
+| className | string | '' | 附加 CSS 类名 |
+| style | object | {} | 附加内联样式 |
+| locale | string | 'zh-CN' | 语言代码 |
+| resetTimerOnUpdate | boolean | false | update() 时重置倒计时 |
+| notifyWhenHidden | boolean | false | 页面不可见时发系统通知 |
+| render | function | — | 自定义渲染函数,完全接管 DOM |
+| onBeforeShow | function | — | 返回 false 阻止显示 |
+| onError | function | — | 钩子/定时器异常全局回调 |
回调函数
| 回调 | 签名 | 触发时机 |
-| onBeforeShow | (toast: ToastInstance) => boolean | void | Toast DOM 创建前。返回 false 阻止显示 |
-| onShow | (toast: ToastInstance) => void | Toast DOM 创建并播放入场动画后 |
-| onClose | (toast: ToastInstance) => void | Toast DOM 被移除后(离场动画完成时) |
-| onClick | (toast: ToastInstance) => void | Toast 被点击时(closeOnClick 为 true 时还会自动关闭) |
-| onUpdate | (toast: ToastInstance) => void | Toast 内容通过 update() 更新后 |
-| onError | ({ hook, source, error, toast }) => void | 钩子回调或定时器发生异常时(全局配置,适用于接入错误监控) |
+| onBeforeShow | (toast) => boolean | void | DOM 创建前,返回 false 阻止显示 |
+| onShow | (toast) => void | 创建并播放入场动画后 |
+| onClose | (toast) => void | DOM 移除后(离场完成) |
+| onClick | (toast) => void | 点击时(closeOnClick=true 时还会关闭) |
+| onUpdate | (toast) => void | update() 后 |
+| onError | ({ hook, source, error, toast }) => void | 钩子/定时器异常时 |
动画列表
-配置 animation 的值,支持以下 CSS 动画。未在此列表中的值将 fallback 到 slide。
+未注册的动画名自动 fallback 到 slide。
| 名称 | 效果 | 时长 |
| slide | 从右侧滑入 + 回弹 | 400ms |
@@ -509,20 +452,13 @@ npm install @metona-team/metona-toast react
| flip | 3D 翻转入场 + 回摆 | 500ms |
| rotate | 旋转摇摆进入 | 500ms |
| zoom | 从中心爆发式弹出 | 500ms |
-| slideUp | 从下方弹入 | 400ms |
-| slideDown | 从上方弹入 | 400ms |
-| slideLeft | 从左侧滑入 | 400ms |
-| slideRight | 从右侧滑入 | 400ms |
+| slideUp / slideDown | 从下方 / 上方弹入 | 400ms |
+| slideLeft / slideRight | 从左侧 / 右侧滑入 | 400ms |
主题参考
-// 内置主题
-light — 白色玻璃质感
-dark — 深色玻璃质感
-auto — 跟随系统主题设置
-warm — 暖色调
-
-// 注册自定义主题
+theme.js
+
// 内置主题:light · dark · auto(跟随系统)· warm
MeToast.themes.registerTheme('ocean', {
bg: 'rgba(240,249,255,0.96)',
text: '#0c4a6e',
@@ -532,19 +468,38 @@ MeToast.themes.registerTheme('ocean'progressBg: 'rgba(14,165,233,0.1)',
closeHoverBg: 'rgba(14,165,233,0.1)',
});
-MeToast.themes.switchTheme('ocean');
+MeToast.themes.
switchTheme(
'ocean');
-