Files
MarkLite/docs/metona-toast-demo.html
T
thzxx 20eb39efc5 release: v0.5.0 — 升级 MetonaEditor 0.4.0 / MetonaToast 0.5.0,数据库迁移 MetonaSqlark 0.4.1
- 编辑器升级 0.4.0:TypeScript 全模块重构、启用浮动格式工具栏
- 通知升级 0.5.0:钩子系统、新增动画
- 移除 Dexie.js,迁移至 MetonaSqlark 0.4.1(AriaEngine:LSM-Tree + WAL + MVCC)
- 数据库更换为 MarkLiteV2,旧 Dexie 数据放弃
- 修复 Editor 组件无条件重渲染(getActiveTab 返回新引用)
- 更新关于弹框、README、DESIGN 文档
2026-08-09 15:45:36 +08:00

547 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<title>在线演示 — MetonaToast</title>
<style>
:root {
--bg: #070b14; --surface: rgba(255,255,255,0.03); --surface2: rgba(255,255,255,0.05);
--border: rgba(255,255,255,0.08); --text: #e8ecf4; --muted: #8b94a7;
--accent: #a5b4fc; --grad: linear-gradient(135deg, #818cf8, #c084fc, #f472b6);
--radius: 14px;
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;
background:var(--bg); color:var(--text); line-height:1.65; min-height:100vh;
background-image:
radial-gradient(ellipse 70% 45% at 50% -10%, rgba(99,102,241,0.12), transparent),
radial-gradient(ellipse 50% 40% at 90% 20%, rgba(168,85,247,0.07), transparent);
}
a { color:var(--accent); text-decoration:none; }
code { background:rgba(99,102,241,0.15); color:#a5b4fc; padding:1px 6px; border-radius:5px; font-size:.85em; }
.grad-text { background:var(--grad); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; color:transparent; }
.nav { position:sticky; top:0; z-index:100; background:rgba(7,11,20,0.72); backdrop-filter:blur(16px); border-bottom:1px solid var(--border); }
.nav-inner { max-width:1320px; margin:0 auto; display:flex; align-items:center; justify-content:space-between; padding:0 22px; height:58px; }
.nav-logo { font-size:18px; font-weight:800; }
.nav-links { display:flex; gap:22px; }
.nav-links a { color:var(--muted); font-size:13.5px; font-weight:500; transition:.2s; }
.nav-links a:hover { color:var(--accent); }
.hero { text-align:center; padding:38px 20px 28px; }
.hero h1 { font-size:34px; font-weight:800; }
.hero p { color:var(--muted); font-size:14px; margin-top:6px; }
.container { max-width:1320px; margin:0 auto; padding:20px; display:grid; grid-template-columns:repeat(auto-fill,minmax(400px,1fr)); gap:16px; }
.card {
background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
padding:20px; transition:border-color .2s, transform .2s; position:relative; overflow:hidden;
}
.card:hover { border-color:rgba(129,140,248,.35); }
.card::before {
content:''; position:absolute; top:0; left:0; right:0; height:2px;
background:var(--grad); opacity:0; transition:opacity .25s;
}
.card:hover::before { opacity:.8; }
.card h2 { font-size:14.5px; margin-bottom:2px; font-weight:700; }
.card > p { color:var(--muted); font-size:11.5px; margin-bottom:12px; }
.btn-row { display:flex; flex-wrap:wrap; gap:6px; }
.btn {
padding:8px 14px; border:none; border-radius:8px; font-size:12px; font-weight:600;
cursor:pointer; transition:transform .15s, filter .15s, box-shadow .15s; color:#fff;
}
.btn:hover { transform:translateY(-1.5px); filter:brightness(1.15); box-shadow:0 6px 18px -6px rgba(0,0,0,.5); }
.btn-sm { padding:5.5px 11px; font-size:11px; border-radius:6px; }
.btn-tiny { padding:4px 9px; font-size:10px; border-radius:5px; }
.btn-outline { background:transparent; border:1px solid var(--border); color:var(--text); }
.btn-outline:hover { border-color:rgba(129,140,248,.5); background:rgba(129,140,248,.08); }
.c-s { background:#10b981; } .c-e { background:#ef4444; } .c-w { background:#f59e0b; color:#1e293b; }
.c-i { background:#3b82f6; } .c-p { background:#6366f1; } .c-pp { background:#8b5cf6; }
.c-pk { background:#ec4899; } .c-t { background:#14b8a6; } .c-o { background:#f97316; }
.c-sl { background:#475569; } .c-r { background:#f43f5e; } .c-g { background:#22c55e; }
.c-grad { background:var(--grad); }
.label {
font-size:10px; text-transform:uppercase; letter-spacing:1px; color:var(--muted);
margin-bottom:10px; font-weight:700;
}
.demo-section { border-top:1px solid var(--border); padding-top:10px; margin-top:10px; }
.status-line { font-size:11.5px; color:#34d399; margin-top:10px; font-family:monospace; min-height:18px; }
footer { text-align:center; padding:30px 20px; color:var(--muted); font-size:12px; border-top:1px solid var(--border); }
@media (max-width:820px) { .container { grid-template-columns:1fr; } }
</style>
</head>
<body>
<nav class="nav">
<div class="nav-inner">
<div class="nav-logo"><span class="grad-text">MetonaToast</span></div>
<div class="nav-links">
<a href="index.html">首页</a>
<a href="docs.html">文档</a>
<a href="demo.html">演示</a>
<a href="https://git.metona.cn/MetonaTeam/MetonaToast" target="_blank">Gitea</a>
</div>
</div>
</nav>
<header class="hero">
<h1><span class="grad-text">在线演示</span></h1>
<p>覆盖项目全部功能的交互演示 — 点击即可体验</p>
</header>
<main class="container">
<!-- 1. 基础通知 -->
<div class="card">
<div class="label">📢 基础通知</div>
<div class="btn-row">
<button class="btn c-s" onclick="MeToast.success('操作成功!')">✓ 成功</button>
<button class="btn c-e" onclick="MeToast.error('操作失败!')">✕ 错误</button>
<button class="btn c-w" onclick="MeToast.warning('请注意检查')">⚠ 警告</button>
<button class="btn c-i" onclick="MeToast.info('系统维护中')"> 信息</button>
<button class="btn c-sl" onclick="MeToast.show('默认消息')">— 默认</button>
</div>
<div class="demo-section">
<div class="btn-row">
<button class="btn btn-outline btn-sm" onclick="MeToast.success({title:'保存成功',message:'文件已同步到云端'})">带标题</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.info('持续 10 秒',{duration:10000})">长时 10s</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.warning('手动关闭',{duration:0,closeButton:true})">手动关闭</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.info('',{icon:false})">无图标</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.info('无关闭按钮',{closeButton:false})">无按钮</button>
<button class="btn btn-outline btn-sm" onclick="demoCustomIcon()">自定义图标</button>
</div>
</div>
</div>
<!-- 2. Loading 链式 -->
<div class="card">
<div class="label">🔄 Loading 链式转换 <span style="color:#34d399">id 稳定</span></div>
<div class="btn-row">
<button class="btn c-s" onclick="demoLoading('success')">提交成功</button>
<button class="btn c-e" onclick="demoLoading('error')">提交失败</button>
<button class="btn c-w" onclick="demoLoading('warning')">转为警告</button>
<button class="btn c-i" onclick="demoLoading('update')">原地更新</button>
</div>
<p class="status-line" id="loading-status">转换后返回的 toast 与 loading 是同一个实例(id 不变)</p>
</div>
<!-- 3. 对话框 -->
<div class="card">
<div class="label">💬 对话框</div>
<div class="btn-row">
<button class="btn c-pp" onclick="demoPromise()">Promise 风格</button>
<button class="btn c-pk" onclick="demoConfirm()">确认对话框</button>
<button class="btn c-t" onclick="demoPrompt()">输入对话框</button>
<button class="btn c-o" onclick="demoPromptPassword()">密码输入</button>
</div>
</div>
<!-- 4. 进度 & 倒计时 -->
<div class="card">
<div class="label">📊 进度 & 倒计时</div>
<div class="btn-row">
<button class="btn c-o" onclick="demoProgress('horizontal')">进度条(水平)</button>
<button class="btn c-g" onclick="demoProgress('vertical')">进度条(垂直)</button>
<button class="btn c-pk" onclick="demoCountdown()">倒计时 5s</button>
<button class="btn c-r" onclick="demoCountdownPause()">倒计时(可暂停)</button>
</div>
</div>
<!-- 5. 队列 & 堆叠 -->
<div class="card">
<div class="label">📋 队列 & 堆叠</div>
<div class="btn-row">
<button class="btn c-p" onclick="demoQueue()">队列展示</button>
<button class="btn c-e" onclick="demoQueueCancel()">队列中途取消</button>
<button class="btn c-pp" onclick="demoStack()">堆叠展示</button>
<button class="btn c-t" onclick="demoStackMixed()">混合类型堆叠</button>
</div>
</div>
<!-- 6. Action Toast -->
<div class="card">
<div class="label">⚡ Action Toast <span style="color:#34d399">close:false 不关闭</span></div>
<div class="btn-row">
<button class="btn c-e" onclick="demoActionDelete()">删除+撤销</button>
<button class="btn c-i" onclick="demoActionMulti()">多按钮</button>
<button class="btn c-s" onclick="demoActionNoClose()">不自动关闭</button>
</div>
</div>
<!-- 7. 分组管理 -->
<div class="card">
<div class="label">📁 分组管理</div>
<div class="btn-row">
<button class="btn c-p" onclick="demoGroup()">创建分组消息</button>
<button class="btn c-e" onclick="dismissGroup()">关闭分组</button>
<button class="btn c-i" onclick="MeToast.info('分组数量: '+group.count(),{type:'info'})">查看数量</button>
<button class="btn btn-outline btn-sm" onclick="demoGroupMixed()">混合类型分组</button>
</div>
</div>
<!-- 8. 动画效果 -->
<div class="card">
<div class="label">🎭 11 种动画效果</div>
<div class="btn-row">
<button class="btn btn-outline btn-tiny" onclick="anim('slide')">slide</button>
<button class="btn btn-outline btn-tiny" onclick="anim('fade')">fade</button>
<button class="btn btn-outline btn-tiny" onclick="anim('scale')">scale</button>
<button class="btn btn-outline btn-tiny" onclick="anim('bounce')">bounce</button>
<button class="btn btn-outline btn-tiny" onclick="anim('flip')">flip</button>
<button class="btn btn-outline btn-tiny" onclick="anim('rotate')">rotate</button>
<button class="btn btn-outline btn-tiny" onclick="anim('zoom')">zoom</button>
<button class="btn btn-outline btn-tiny" onclick="anim('slideUp')">slideUp</button>
<button class="btn btn-outline btn-tiny" onclick="anim('slideDown')">slideDown</button>
<button class="btn btn-outline btn-tiny" onclick="anim('slideLeft')">slideLeft</button>
<button class="btn btn-outline btn-tiny" onclick="anim('slideRight')">slideRight</button>
</div>
<div class="demo-section">
<div class="btn-row">
<button class="btn c-grad" onclick="demoCustomAnim()">✨ 注册自定义动画</button>
</div>
</div>
</div>
<!-- 9. 位置 -->
<div class="card">
<div class="label">📍 全部位置</div>
<div class="btn-row">
<button class="btn btn-outline btn-sm" onclick="MeToast.success('左上角',{position:'top-left'})">↖ top-left</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.success('顶部居中',{position:'top-center'})">↑ top-center</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.success('右上角',{position:'top-right'})">↗ top-right</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.success('左下角',{position:'bottom-left'})">↙ bottom-left</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.success('底部居中',{position:'bottom-center'})">↓ bottom-center</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.success('右下角',{position:'bottom-right'})">↘ bottom-right</button>
</div>
</div>
<!-- 10. 主题 -->
<div class="card">
<div class="label">🎨 主题切换 & 自定义</div>
<div class="btn-row">
<button class="btn btn-outline btn-sm" onclick="switchTheme('light')">☀️ Light</button>
<button class="btn btn-outline btn-sm" onclick="switchTheme('dark')">🌙 Dark</button>
<button class="btn btn-outline btn-sm" onclick="switchTheme('auto')">🔄 Auto</button>
<button class="btn btn-outline btn-sm" onclick="switchTheme('warm')">🔥 Warm</button>
<button class="btn btn-outline btn-sm" onclick="demoRegisterTheme()">注册自定义</button>
</div>
</div>
<!-- 11. 插件 -->
<div class="card">
<div class="label">🔌 插件系统 <span style="color:#34d399">4 款预设</span></div>
<div class="btn-row">
<button class="btn btn-outline btn-sm" onclick="MeToast.use('keyboard');MeToast.info('已安装 — 按 ESC 关闭所有 Toast')">keyboard</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.use('persistence');MeToast.info('已安装 — 配置持久化')">persistence</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.use('accessibility');MeToast.info('已安装 — 屏幕阅读器')">accessibility</button>
<button class="btn btn-outline btn-sm" onclick="MeToast.use('dedupe');MeToast.info('已安装 — 同消息去重')">dedupe</button>
<button class="btn btn-outline btn-sm" onclick="demoCustomPlugin()">自定义插件</button>
</div>
<div class="demo-section">
<div class="btn-row">
<button class="btn c-t btn-sm" onclick="demoDedupe()">去重演示</button>
</div>
</div>
</div>
<!-- 12. 钩子系统 -->
<div class="card">
<div class="label">🔗 钩子系统 <span style="color:#34d399">beforeShow 拦截演示</span></div>
<div class="btn-row">
<button class="btn c-p" onclick="demoHookBlock()">开启拦截 error</button>
<button class="btn c-e" onclick="MeToast.error('这条会被拦截')">触发被拦 error</button>
<button class="btn c-s" onclick="MeToast.success('这条不受影响')">正常 success</button>
<button class="btn btn-outline btn-sm" onclick="demoHookUnblock()">关闭拦截</button>
<button class="btn btn-outline btn-sm" onclick="demoHookCount()">钩子统计</button>
</div>
<p class="status-line" id="hook-status">钩子:未注册</p>
</div>
<!-- 13. 高级特性 -->
<div class="card">
<div class="label">🔥 高级特性</div>
<div class="btn-row">
<button class="btn c-i" onclick="demoResetTimer()">resetTimerOnUpdate</button>
<button class="btn c-p" onclick="demoOnUpdate()">onUpdate 回调</button>
<button class="btn c-pp" onclick="demoCustomRender()">自定义渲染</button>
<button class="btn c-w" onclick="demoNotify()">系统通知</button>
</div>
</div>
<!-- 14. Toast 管理 -->
<div class="card">
<div class="label">🔧 Toast 管理</div>
<div class="btn-row">
<button class="btn c-s" onclick="MeToast.success('消息 '+(MeToast.count()+1))">发一条</button>
<button class="btn c-i" onclick="MeToast.info('消息 '+(MeToast.count()+1))">再一条</button>
<button class="btn c-w" onclick="MeToast.info('当前 '+MeToast.count()+' 条')">查看数量</button>
<button class="btn c-sl" onclick="MeToast.pauseAll();MeToast.info('已暂停全部')">暂停全部</button>
<button class="btn c-sl" onclick="MeToast.resumeAll();MeToast.info('已恢复全部')">恢复全部</button>
<button class="btn c-e" onclick="MeToast.dismiss()">关闭全部</button>
<button class="btn c-r" onclick="MeToast.destroy();MeToast.init({});MeToast.info('已销毁并重建')">销毁重建</button>
</div>
<div class="demo-section">
<div class="btn-row">
<button class="btn btn-outline btn-tiny" onclick="demoFindById()">findById</button>
<button class="btn btn-outline btn-tiny" onclick="demoGetAll()">getAll</button>
<button class="btn btn-outline btn-tiny" onclick="demoClearByPos()">clear(位置)</button>
<button class="btn btn-outline btn-tiny" onclick="demoGetConfig()">查看配置</button>
<button class="btn btn-outline btn-tiny" onclick="demoRemove()">removeToast 立即移除</button>
</div>
</div>
</div>
<!-- 15. onError -->
<div class="card">
<div class="label">🛡️ onError 错误捕获</div>
<div class="btn-row">
<button class="btn c-e" onclick="demoOnError()">演示 onError</button>
<button class="btn c-w" onclick="demoOnErrorReset()">重置回调</button>
</div>
</div>
<!-- 16. 交互特性 -->
<div class="card">
<div class="label">✋ 交互特性</div>
<div class="btn-row">
<button class="btn c-i" onclick="demoDraggable()">拖拽关闭</button>
<button class="btn c-p" onclick="demoDragThreshold()">拖拽阈值 300px</button>
<button class="btn c-w" onclick="demoPauseHover()">悬停暂停</button>
<button class="btn c-pp" onclick="demoCloseOnClick()">点击关闭</button>
<button class="btn c-t" onclick="demoWidth()">自定义宽度</button>
<button class="btn c-sl" onclick="demoClassName()">自定义 className</button>
</div>
</div>
<!-- 17. 国际化 -->
<div class="card">
<div class="label">🌍 国际化 & 格式化</div>
<div class="btn-row">
<button class="btn c-s" onclick="switchLocale('zh-CN')">切换到中文</button>
<button class="btn c-i" onclick="switchLocale('en-US')">Switch to English</button>
<button class="btn btn-outline btn-sm" onclick="demoFormat()">格式化演示</button>
</div>
</div>
<!-- 18. 图标网格 -->
<div class="card" style="grid-column:1/-1;">
<div class="label">🎨 107 种预设图标 — 点击任意图标发送 Toast</div>
<div class="btn-row" id="icon-grid" style="max-height:320px;overflow-y:auto;padding:4px 0;"></div>
</div>
</main>
<footer>
MetonaToast v0.5.0 · MIT ·
<a href="index.html">首页</a> ·
<a href="docs.html">文档</a> ·
<a href="https://git.metona.cn/MetonaTeam/MetonaToast">Gitea</a>
</footer>
<script src="../dist/metona-toast.js"></script>
<script>
MeToast.configure({ position:'top-right', duration:4000, theme:'dark', animation:'slide', pauseOnHover:true, showProgress:true });
// === Loading ===
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(_){}
}
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(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.resume();MeToast.info('已恢复倒计时')},7000);
}
// === Queue & Stack ===
async function demoQueue() { await MeToast.queue(['第一步','第二步','第三步'],{delay:800,duration:2000,type:'info'}); }
async function demoQueueCancel() {
const q=MeToast.queue(['步骤1','步骤2','步骤3','步骤4','步骤5'],{delay:600,duration:1500,type:'info'});
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}); }
// === 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() {
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');
function demoGroup() { group.success('分组成功 #'+group.count()); group.info('分组消息 #'+group.count()); }
function dismissGroup() { MeToast.dismissGroup('demo-group'); MeToast.info('分组已关闭'); }
function demoGroupMixed() {
const g = MeToast.group('mixed-test');
g.success('OK'); g.warning('Warning'); g.info('Info');
setTimeout(()=>MeToast.dismissGroup('mixed-test'),4000);
}
// === 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.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.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('这条不会自动消失(每次更新重置 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); }
function demoCustomRender() {
MeToast.show({render:toast=>'<div style="background:#111;color:#0f0;padding:16px 20px;border-radius:10px;border:1px solid #0f0;font-family:monospace">🟢 <b>'+toast.message+'</b><br><span style="font-size:11px;opacity:0.6"># custom render</span></div>',message:'自定义渲染内容',duration:5000});
}
function demoNotify() {
if(typeof Notification==='undefined'){MeToast.error('浏览器不支持 Notification');return;}
if(Notification.permission==='denied'){MeToast.error('通知权限已拒绝');return;}
MeToast.info('页面隐藏时看系统通知',{notifyWhenHidden:true,duration:6000});
}
// === Management ===
function demoFindById() {
const t=MeToast.info('这是测试 toast',{duration:5000});
setTimeout(()=>{const found=MeToast.find(t.id); if(found)MeToast.success('找到了 #'+found.id);},500);
}
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() { 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 });
var badFn = function() { throw new Error('demo-error'); };
Toast.on('afterShow', badFn);
MeToast.success('这条会触发错误回调', { duration: 2000 });
setTimeout(function() { Toast.off('afterShow', badFn); }, 3000);
}
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: '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/></svg>' });
}
// === i18n ===
function switchLocale(loc) {
MeToast.i18n.switchLocale(loc); MeToast.configure({locale:loc});
MeToast.success(loc==='zh-CN'?'已切换到中文':'Switched to English');
}
function demoFormat() {
MeToast.info('数字: '+MeToast.i18n.formatNumber(1234567)+' | 货币: '+MeToast.i18n.formatCurrency(99.5,'CNY')+' | 日期: '+MeToast.i18n.formatDate(new Date()),{duration:8000});
}
// === 图标网格 ===
(function renderIcons() {
var grid = document.getElementById('icon-grid');
if (!grid) return;
var icons = ['success','error','warning','info','loading','close','check','x','alert','question','star','heart','bell','mail','settings','user','home','search','plus','minus','edit','trash','download','upload','share','link','external','clock','calendar','map','compass','globe','wifi','cloud','sun','moon','zap','activity','cpu','database','server','terminal','code','git','package','layers','grid','list','filter','sort','refresh','sync','power','battery','bluetooth','volume','mic','camera','image','video','music','file','folder','clipboard','save','print','eye','eyeOff','lock','unlock','shield','key','flag','bookmark','tag','gift','award','target','crosshair','move','maximize','minimize','copy','cut','paste','rotateCw','rotateCcw','zoomIn','zoomOut','crop','sliders','toggleLeft','toggleRight','checkCircle','xCircle','alertCircle','infoCircle','helpCircle','alertTriangle','checkSquare','square','circle','triangle','hexagon','octagon','pentagon','diamond'];
icons.forEach(function(ic) {
var btn = document.createElement('button');
btn.className = 'btn btn-outline btn-tiny';
btn.textContent = ic;
btn.title = 'type=' + ic;
btn.onclick = function() { MeToast.success(ic, { type: ic, duration: 2000, showProgress: false }); };
grid.appendChild(btn);
});
})();
</script>
</body>
</html>