refactor: examples/→site/ 官网+文档+演示三件套; 更新build.sh/rollup/serve.sh路径

This commit is contained in:
tianhao
2026-06-16 13:32:14 +08:00
parent 86bfcc4102
commit 3ab10f89a4
8 changed files with 843 additions and 842 deletions
+2 -1
View File
@@ -78,5 +78,6 @@ echo " 2. ES Module: import MeToast from 'dist/metona-toast.esm.js'"
echo " 3. CommonJS: const MeToast = require('dist/metona-toast.cjs.js')"
echo ""
echo "📝 示例:"
echo " 打开 examples/demo.html 查看完整示例"
echo " 打开 site/index.html 查看官网"
echo " 打开 site/demo.html 查看演示"
echo ""
-395
View File
@@ -1,395 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MetonaToast — 全功能演示</title>
<style>
:root {
--bg: #0b1121; --surface: #151d33; --border: #1e2d4d;
--text: #e2e8f0; --muted: #64748b; --accent: #6366f1; --accent-light: #818cf8;
--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.6; min-height: 100vh;
}
.hero {
text-align: center; padding: 70px 20px 50px;
background: radial-gradient(ellipse at 50% 0%, rgba(99,102,241,0.15) 0%, transparent 60%),
linear-gradient(180deg, #0f172a 0%, var(--bg) 100%);
border-bottom: 1px solid var(--border);
}
.hero h1 { font-size: 42px; font-weight: 800; }
.hero h1 span {
background: linear-gradient(135deg, #818cf8, #c084fc, #f472b6);
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.hero p { color: var(--muted); font-size: 15px; margin-top: 6px; }
.nav {
position: sticky; top: 0; z-index: 100;
background: rgba(11,17,33,0.85); backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border);
}
.nav-inner { max-width: 1300px; margin: 0 auto; display: flex; overflow-x: auto; padding: 0 20px; gap: 4px; }
.nav a {
padding: 14px 16px; color: var(--muted); text-decoration: none; font-size: 13px;
font-weight: 500; white-space: nowrap; border-bottom: 2px solid transparent; transition: 0.2s;
}
.nav a:hover, .nav a.active { color: var(--accent-light); border-bottom-color: var(--accent); }
.container { max-width: 1300px; margin: 0 auto; padding: 40px 20px; }
.section { margin-bottom: 56px; }
.section-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.section-icon {
width: 42px; height: 42px; border-radius: 10px;
background: linear-gradient(135deg, var(--accent), #8b5cf6);
display: flex; align-items: center; justify-content: center; font-size: 20px;
}
.section-title { font-size: 24px; font-weight: 700; }
.section-desc { color: var(--muted); font-size: 14px; margin-bottom: 24px; }
.card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 24px; margin-bottom: 16px;
}
.card-label {
font-size: 12px; font-weight: 600; text-transform: uppercase;
letter-spacing: 0.8px; color: var(--muted); margin-bottom: 16px;
}
.btn-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.btn-grid-sm { grid-template-columns: repeat(auto-fill, minmax(95px, 1fr)); }
.btn {
padding: 10px 16px; border: none; border-radius: 8px; font-size: 13px;
font-weight: 500; cursor: pointer; transition: 0.2s; color: #fff;
display: flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.15); }
.btn-sm { padding: 7px 12px; font-size: 12px; border-radius: 6px; }
.btn-outline { background: transparent; border: 1px solid var(--border); color: var(--text); }
.btn-outline:hover { background: var(--border); }
.c-success { background: #10b981; } .c-error { background: #ef4444; }
.c-warning { background: #f59e0b; color: #1e293b; } .c-info { background: #3b82f6; }
.c-primary { background: #6366f1; } .c-purple { background: #8b5cf6; }
.c-pink { background: #ec4899; } .c-orange { background: #f97316; }
.c-teal { background: #14b8a6; } .c-cyan { background: #06b6d4; }
.c-rose { background: #f43f5e; } .c-slate { background: #475569; }
.c-indigo { background: #4f46e5; }
.theme-swatch {
display: inline-flex; align-items: center; gap: 8px;
padding: 8px 14px; border-radius: 8px; border: 1px solid var(--border);
cursor: pointer; transition: 0.2s; font-size: 13px; font-weight: 500;
}
.theme-swatch:hover { border-color: var(--accent); }
.swatch-dot { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(255,255,255,0.2); }
.theme-grid { display: flex; flex-wrap: wrap; gap: 10px; }
footer { text-align: center; color: var(--muted); font-size: 13px; padding: 40px 20px; border-top: 1px solid var(--border); }
footer a { color: var(--accent-light); text-decoration: none; }
@media (max-width: 768px) {
.hero h1 { font-size: 28px; }
.btn-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
}
</style>
</head>
<body>
<header class="hero">
<h1><span>MetonaToast</span></h1>
<p>轻量 · 零依赖 · 精致美观 — 全功能演示</p>
</header>
<nav class="nav">
<div class="nav-inner">
<a href="#types">图标类型</a>
<a href="#positions">位置</a>
<a href="#animations">动画</a>
<a href="#themes">主题</a>
<a href="#advanced">高级</a>
<a href="#plugins">插件</a>
<a href="#i18n">国际化</a>
<a href="#management">管理</a>
</div>
</nav>
<main class="container">
<!-- ===== TYPES ===== -->
<section id="types" class="section">
<div class="section-head"><div class="section-icon">🎨</div><div><h2 class="section-title">图标类型</h2><p class="section-desc">80+ 内置图标类型</p></div></div>
<div class="card">
<div class="card-label">常用</div>
<div class="btn-grid">
<button class="btn c-success" onclick="showType('success')">✓ success</button>
<button class="btn c-error" onclick="showType('error')">✕ error</button>
<button class="btn c-warning" onclick="showType('warning')">⚠ warning</button>
<button class="btn c-info" onclick="showType('info')"> info</button>
<button class="btn c-slate" onclick="showType('default')">— default</button>
<button class="btn c-purple" onclick="showType('loading')">⟳ loading</button>
</div>
</div>
<div class="card"><div class="card-label">操作</div>
<div class="btn-grid btn-grid-sm">
<button class="btn btn-outline btn-sm" onclick="showType('check')">check</button>
<button class="btn btn-outline btn-sm" onclick="showType('x')">x</button>
<button class="btn btn-outline btn-sm" onclick="showType('alert')">alert</button>
<button class="btn btn-outline btn-sm" onclick="showType('question')">question</button>
<button class="btn btn-outline btn-sm" onclick="showType('star')">star</button>
<button class="btn btn-outline btn-sm" onclick="showType('heart')">heart</button>
<button class="btn btn-outline btn-sm" onclick="showType('bell')">bell</button>
<button class="btn btn-outline btn-sm" onclick="showType('mail')">mail</button>
<button class="btn btn-outline btn-sm" onclick="showType('settings')">settings</button>
<button class="btn btn-outline btn-sm" onclick="showType('user')">user</button>
<button class="btn btn-outline btn-sm" onclick="showType('home')">home</button>
<button class="btn btn-outline btn-sm" onclick="showType('search')">search</button>
<button class="btn btn-outline btn-sm" onclick="showType('plus')">plus</button>
<button class="btn btn-outline btn-sm" onclick="showType('minus')">minus</button>
<button class="btn btn-outline btn-sm" onclick="showType('edit')">edit</button>
<button class="btn btn-outline btn-sm" onclick="showType('trash')">trash</button>
<button class="btn btn-outline btn-sm" onclick="showType('download')">download</button>
<button class="btn btn-outline btn-sm" onclick="showType('upload')">upload</button>
<button class="btn btn-outline btn-sm" onclick="showType('share')">share</button>
<button class="btn btn-outline btn-sm" onclick="showType('link')">link</button>
<button class="btn btn-outline btn-sm" onclick="showType('external')">external</button>
<button class="btn btn-outline btn-sm" onclick="showType('copy')">copy</button>
<button class="btn btn-outline btn-sm" onclick="showType('cut')">cut</button>
<button class="btn btn-outline btn-sm" onclick="showType('paste')">paste</button>
<button class="btn btn-outline btn-sm" onclick="showType('save')">save</button>
<button class="btn btn-outline btn-sm" onclick="showType('print')">print</button>
</div>
</div>
<div class="card"><div class="card-label">系统 & 设备</div>
<div class="btn-grid btn-grid-sm">
<button class="btn btn-outline btn-sm" onclick="showType('cpu')">cpu</button>
<button class="btn btn-outline btn-sm" onclick="showType('database')">database</button>
<button class="btn btn-outline btn-sm" onclick="showType('server')">server</button>
<button class="btn btn-outline btn-sm" onclick="showType('terminal')">terminal</button>
<button class="btn btn-outline btn-sm" onclick="showType('code')">code</button>
<button class="btn btn-outline btn-sm" onclick="showType('git')">git</button>
<button class="btn btn-outline btn-sm" onclick="showType('package')">package</button>
<button class="btn btn-outline btn-sm" onclick="showType('layers')">layers</button>
<button class="btn btn-outline btn-sm" onclick="showType('grid')">grid</button>
<button class="btn btn-outline btn-sm" onclick="showType('list')">list</button>
<button class="btn btn-outline btn-sm" onclick="showType('filter')">filter</button>
<button class="btn btn-outline btn-sm" onclick="showType('sort')">sort</button>
<button class="btn btn-outline btn-sm" onclick="showType('refresh')">refresh</button>
<button class="btn btn-outline btn-sm" onclick="showType('sync')">sync</button>
<button class="btn btn-outline btn-sm" onclick="showType('wifi')">wifi</button>
<button class="btn btn-outline btn-sm" onclick="showType('bluetooth')">bluetooth</button>
<button class="btn btn-outline btn-sm" onclick="showType('battery')">battery</button>
<button class="btn btn-outline btn-sm" onclick="showType('power')">power</button>
<button class="btn btn-outline btn-sm" onclick="showType('volume')">volume</button>
<button class="btn btn-outline btn-sm" onclick="showType('mic')">mic</button>
<button class="btn btn-outline btn-sm" onclick="showType('camera')">camera</button>
<button class="btn btn-outline btn-sm" onclick="showType('image')">image</button>
<button class="btn btn-outline btn-sm" onclick="showType('video')">video</button>
<button class="btn btn-outline btn-sm" onclick="showType('music')">music</button>
<button class="btn btn-outline btn-sm" onclick="showType('file')">file</button>
<button class="btn btn-outline btn-sm" onclick="showType('folder')">folder</button>
<button class="btn btn-outline btn-sm" onclick="showType('clipboard')">clipboard</button>
<button class="btn btn-outline btn-sm" onclick="showType('eye')">eye</button>
<button class="btn btn-outline btn-sm" onclick="showType('eyeOff')">eyeOff</button>
<button class="btn btn-outline btn-sm" onclick="showType('lock')">lock</button>
<button class="btn btn-outline btn-sm" onclick="showType('unlock')">unlock</button>
<button class="btn btn-outline btn-sm" onclick="showType('shield')">shield</button>
<button class="btn btn-outline btn-sm" onclick="showType('key')">key</button>
</div>
</div>
<div class="card"><div class="card-label">自然 & 几何</div>
<div class="btn-grid btn-grid-sm">
<button class="btn btn-outline btn-sm" onclick="showType('cloud')">cloud</button>
<button class="btn btn-outline btn-sm" onclick="showType('sun')">sun</button>
<button class="btn btn-outline btn-sm" onclick="showType('moon')">moon</button>
<button class="btn btn-outline btn-sm" onclick="showType('zap')">zap</button>
<button class="btn btn-outline btn-sm" onclick="showType('compass')">compass</button>
<button class="btn btn-outline btn-sm" onclick="showType('globe')">globe</button>
<button class="btn btn-outline btn-sm" onclick="showType('map')">map</button>
<button class="btn btn-outline btn-sm" onclick="showType('clock')">clock</button>
<button class="btn btn-outline btn-sm" onclick="showType('calendar')">calendar</button>
<button class="btn btn-outline btn-sm" onclick="showType('flag')">flag</button>
<button class="btn btn-outline btn-sm" onclick="showType('bookmark')">bookmark</button>
<button class="btn btn-outline btn-sm" onclick="showType('tag')">tag</button>
<button class="btn btn-outline btn-sm" onclick="showType('gift')">gift</button>
<button class="btn btn-outline btn-sm" onclick="showType('award')">award</button>
<button class="btn btn-outline btn-sm" onclick="showType('target')">target</button>
<button class="btn btn-outline btn-sm" onclick="showType('crosshair')">crosshair</button>
<button class="btn btn-outline btn-sm" onclick="showType('checkCircle')">checkCircle</button>
<button class="btn btn-outline btn-sm" onclick="showType('xCircle')">xCircle</button>
<button class="btn btn-outline btn-sm" onclick="showType('alertCircle')">alertCircle</button>
<button class="btn btn-outline btn-sm" onclick="showType('infoCircle')">infoCircle</button>
<button class="btn btn-outline btn-sm" onclick="showType('helpCircle')">helpCircle</button>
<button class="btn btn-outline btn-sm" onclick="showType('alertTriangle')">alertTriangle</button>
<button class="btn btn-outline btn-sm" onclick="showType('checkSquare')">checkSquare</button>
<button class="btn btn-outline btn-sm" onclick="showType('square')">square</button>
<button class="btn btn-outline btn-sm" onclick="showType('circle')">circle</button>
<button class="btn btn-outline btn-sm" onclick="showType('triangle')">triangle</button>
<button class="btn btn-outline btn-sm" onclick="showType('hexagon')">hexagon</button>
<button class="btn btn-outline btn-sm" onclick="showType('octagon')">octagon</button>
<button class="btn btn-outline btn-sm" onclick="showType('pentagon')">pentagon</button>
<button class="btn btn-outline btn-sm" onclick="showType('diamond')">diamond</button>
</div>
</div>
</section>
<!-- ===== POSITIONS ===== -->
<section id="positions" class="section">
<div class="section-head"><div class="section-icon">📍</div><div><h2 class="section-title">位置</h2><p class="section-desc">6 种预设位置</p></div></div>
<div class="card">
<div class="btn-grid">
<button class="btn c-slate" onclick="MeToast.success('左上角',{position:'top-left'})">↖ top-left</button>
<button class="btn c-slate" onclick="MeToast.success('顶部居中',{position:'top-center'})">↑ top-center</button>
<button class="btn c-slate" onclick="MeToast.success('右上角',{position:'top-right'})">↗ top-right</button>
<button class="btn c-slate" onclick="MeToast.success('左下角',{position:'bottom-left'})">↙ bottom-left</button>
<button class="btn c-slate" onclick="MeToast.success('底部居中',{position:'bottom-center'})">↓ bottom-center</button>
<button class="btn c-slate" onclick="MeToast.success('右下角',{position:'bottom-right'})">↘ bottom-right</button>
</div>
</div>
</section>
<!-- ===== ANIMATIONS ===== -->
<section id="animations" class="section">
<div class="section-head"><div class="section-icon">🎭</div><div><h2 class="section-title">动画效果</h2><p class="section-desc">11 种动画,每种效果明显不同</p></div></div>
<div class="card">
<div class="btn-grid">
<button class="btn c-primary" onclick="anim('slide')">slide 右侧滑入</button>
<button class="btn c-info" onclick="anim('fade')">fade 淡入模糊</button>
<button class="btn c-success" onclick="anim('scale')">scale 弹性放大</button>
<button class="btn c-warning" onclick="anim('bounce')">bounce 弹跳</button>
<button class="btn c-purple" onclick="anim('flip')">flip 3D翻开</button>
<button class="btn c-pink" onclick="anim('rotate')">rotate 旋转</button>
<button class="btn c-teal" onclick="anim('zoom')">zoom 爆发</button>
<button class="btn c-orange" onclick="anim('slideUp')">slideUp ↑</button>
<button class="btn c-cyan" onclick="anim('slideDown')">slideDown ↓</button>
<button class="btn c-rose" onclick="anim('slideLeft')">slideLeft ←</button>
<button class="btn c-indigo" onclick="anim('slideRight')">slideRight →</button>
</div>
</div>
</section>
<!-- ===== THEMES ===== -->
<section id="themes" class="section">
<div class="section-head"><div class="section-icon">🎨</div><div><h2 class="section-title">主题</h2><p class="section-desc">四种基础主题,支持 registerTheme() 自定义注册</p></div></div>
<div class="card">
<div class="card-label">基础主题</div>
<div class="theme-grid">
<div class="theme-swatch" onclick="switchTheme('light')"><span class="swatch-dot" style="background:#fff"></span>浅色 light</div>
<div class="theme-swatch" onclick="switchTheme('dark')"><span class="swatch-dot" style="background:#1c2028"></span>深色 dark</div>
<div class="theme-swatch" onclick="switchTheme('auto')"><span class="swatch-dot" style="background:linear-gradient(135deg,#fff,#1c2028)"></span>自动 auto</div>
<div class="theme-swatch" onclick="switchTheme('warm')"><span class="swatch-dot" style="background:#fffbeb"></span>暖色 warm</div>
</div>
</div>
</section>
<!-- ===== ADVANCED ===== -->
<section id="advanced" class="section">
<div class="section-head"><div class="section-icon"></div><div><h2 class="section-title">高级功能</h2><p class="section-desc">Promise / 确认框 / 输入框 / 进度条 / 倒计时 / 队列 / 堆叠</p></div></div>
<div class="card">
<div class="btn-grid">
<button class="btn c-purple" onclick="demoPromise()">Promise 风格</button>
<button class="btn c-pink" onclick="demoConfirm()">确认对话框</button>
<button class="btn c-teal" onclick="demoPrompt()">输入对话框</button>
<button class="btn c-orange" onclick="demoProgress()">进度条</button>
<button class="btn c-cyan" onclick="demoCountdown()">倒计时 5s</button>
<button class="btn c-primary" onclick="demoQueue()">队列展示</button>
<button class="btn c-indigo" onclick="demoStack()">堆叠展示</button>
</div>
</div>
</section>
<!-- ===== PLUGINS ===== -->
<section id="plugins" class="section">
<div class="section-head"><div class="section-icon">🔌</div><div><h2 class="section-title">插件系统</h2><p class="section-desc">3 款预设插件,按需安装</p></div></div>
<div class="card">
<div class="btn-grid">
<button class="btn c-purple" onclick="usePlugin('keyboard')">keyboard ESC关闭</button>
<button class="btn c-info" onclick="usePlugin('persistence')">persistence 持久化</button>
<button class="btn c-teal" onclick="usePlugin('accessibility')">accessibility 无障碍</button>
</div>
</div>
</section>
<!-- ===== I18N ===== -->
<section id="i18n" class="section">
<div class="section-head"><div class="section-icon">🌍</div><div><h2 class="section-title">国际化</h2><p class="section-desc">切换语言查看效果</p></div></div>
<div class="card">
<div class="btn-grid">
<button class="btn c-primary" onclick="switchLang('zh-CN')">简体中文</button>
<button class="btn c-info" onclick="switchLang('en-US')">English</button>
<button class="btn c-success" onclick="switchLang('ja')">日本語</button>
<button class="btn c-purple" onclick="switchLang('ko')">한국어</button>
<button class="btn c-pink" onclick="switchLang('fr')">Français</button>
<button class="btn c-teal" onclick="switchLang('de')">Deutsch</button>
<button class="btn c-orange" onclick="switchLang('es')">Español</button>
<button class="btn c-cyan" onclick="switchLang('ru')">Русский</button>
<button class="btn c-rose" onclick="switchLang('ar')">العربية</button>
</div>
</div>
</section>
<!-- ===== MANAGEMENT ===== -->
<section id="management" class="section">
<div class="section-head"><div class="section-icon">🔧</div><div><h2 class="section-title">Toast 管理</h2><p class="section-desc">统计、关闭、清除</p></div></div>
<div class="card">
<div class="btn-grid">
<button class="btn c-success" onclick="MeToast.success('消息 '+Date.now()%100)">发一条消息</button>
<button class="btn c-info" onclick="MeToast.info('消息 '+Date.now()%100)">再发一条</button>
<button class="btn c-warning" onclick="alert('当前: '+MeToast.count()+' 条')">查看数量</button>
<button class="btn c-error" onclick="MeToast.dismiss()">关闭全部</button>
<button class="btn c-slate" onclick="MeToast.clear()">清除全部</button>
<button class="btn c-purple" onclick="MeToast.pauseAll()">暂停全部</button>
<button class="btn c-teal" onclick="MeToast.resumeAll()">恢复全部</button>
</div>
</div>
</section>
</main>
<footer>
<p>MetonaToast v2.0.0 · <a href="index.html">← 代码示例</a></p>
</footer>
<script src="../dist/metona-toast.js"></script>
<script>
MeToast.configure({ position: 'top-right', duration: 4000, theme: 'dark', animation: 'slide', pauseOnHover: true, showProgress: true });
function showType(type) {
if (type === 'loading') { const l = MeToast.loading('加载中...'); setTimeout(() => l.success('完成!'), 2000); return; }
if (type === 'success') { MeToast.success(type); return; }
if (type === 'error') { MeToast.error(type); return; }
if (type === 'warning') { MeToast.warning(type); return; }
if (type === 'info') { MeToast.info(type); return; }
if (type === 'default') { MeToast.show('默认消息'); return; }
MeToast.show(type, { type });
}
function anim(name) { MeToast.success('动画: ' + name, { animation: name, duration: 3000 }); }
function switchTheme(t) { MeToast.themes.switchTheme(t); MeToast.configure({ theme: t }); MeToast.info('主题: ' + t, { theme: t }); }
function switchLang(l) { MeToast.i18n.switchLocale(l); MeToast.success(MeToast.i18n.getLocaleName(l) + ' (' + l + ')'); }
function usePlugin(n) { MeToast.use(n); MeToast.success('插件: ' + n); }
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('已删除'); else MeToast.info('已取消');
}
async function demoPrompt() {
const v = await MeToast.prompt('请输入姓名', { placeholder: '请输入...' });
if (v) MeToast.info('你好,' + v + '');
}
function demoProgress() {
const p = MeToast.progress('上传中...');
let v = 0;
const t = setInterval(() => { v += Math.random() * 28; if (v >= 100) { clearInterval(t); p.complete('完成!'); } else p.setProgress(v); }, 400);
}
function demoCountdown() {
MeToast.countdown('{seconds} 秒后执行', 5, { onComplete: () => MeToast.success('已执行') });
}
async function demoQueue() {
await MeToast.queue(['步骤一', '步骤二', '步骤三'], { delay: 800, duration: 2000, type: 'info' });
}
function demoStack() {
MeToast.stack(['消息 1', '消息 2', '消息 3', '消息 4'], { stagger: 150, type: 'info' });
}
</script>
</body>
</html>
-442
View File
@@ -1,442 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MetonaToast — 基础功能与代码示例</title>
<style>
:root {
--bg: #0b1121; --surface: #151d33; --border: #1e2d4d;
--text: #e2e8f0; --muted: #64748b; --accent: #6366f1; --accent-light: #818cf8;
--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.6; min-height: 100vh;
}
.hero {
text-align: center; padding: 80px 20px 60px;
background: radial-gradient(ellipse at 50% 0%, rgba(99,102,241,0.15) 0%, transparent 60%),
linear-gradient(180deg, #0f172a 0%, var(--bg) 100%);
border-bottom: 1px solid var(--border);
}
.hero h1 { font-size: 48px; font-weight: 800; }
.hero h1 span {
background: linear-gradient(135deg, #818cf8, #c084fc, #f472b6);
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.hero p { color: var(--muted); font-size: 16px; margin-top: 8px; }
.hero .badges { display: flex; gap: 12px; justify-content: center; margin-top: 20px; flex-wrap: wrap; }
.hero .badges span {
background: var(--surface); border: 1px solid var(--border); border-radius: 20px;
padding: 5px 16px; font-size: 13px; color: var(--accent-light);
}
.container { max-width: 900px; margin: 0 auto; padding: 40px 20px; }
.section { margin-bottom: 48px; }
.section h2 { font-size: 22px; margin-bottom: 4px; }
.section > p { color: var(--muted); font-size: 14px; margin-bottom: 20px; }
.card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 24px; margin-bottom: 16px;
}
.card h3 { font-size: 14px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 14px; }
.btn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.btn {
padding: 9px 18px; border: none; border-radius: 8px; font-size: 13px;
font-weight: 500; cursor: pointer; transition: 0.2s; color: #fff;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.15); }
.c-success { background: #10b981; } .c-error { background: #ef4444; }
.c-warning { background: #f59e0b; color: #1e293b; } .c-info { background: #3b82f6; }
.c-primary { background: #6366f1; } .c-purple { background: #8b5cf6; }
.c-pink { background: #ec4899; } .c-teal { background: #14b8a6; }
.c-orange { background: #f97316; } .c-slate { background: #475569; }
pre {
background: #0a0f1a; border: 1px solid var(--border); border-radius: 8px;
padding: 14px 18px; overflow-x: auto; margin: 0;
font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;
font-size: 12.5px; line-height: 1.75; color: #cbd5e1;
}
pre .kw { color: #c084fc; } pre .fn { color: #60a5fa; }
pre .s { color: #34d399; } pre .cm { color: #475569; }
.api-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.api-table th, .api-table td { text-align: left; padding: 7px 12px; border-bottom: 1px solid var(--border); }
.api-table th { color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; }
.api-table code { background: rgba(99,102,241,0.15); color: #a5b4fc; padding: 1px 6px; border-radius: 4px; font-size: 12px; }
footer { text-align: center; color: var(--muted); font-size: 13px; padding: 40px 20px; border-top: 1px solid var(--border); }
footer a { color: var(--accent-light); text-decoration: none; }
</style>
</head>
<body>
<header class="hero">
<h1><span>MetonaToast</span></h1>
<p>轻量 · 零依赖 · 精致美观的 Toast 通知库</p>
<div class="badges">
<span>📦 &lt;10KB gzip</span><span>🎨 80+ 图标</span><span>🎭 11 动画</span><span>🌍 国际化</span><span>🔌 插件</span>
</div>
</header>
<main class="container">
<!-- 安装 -->
<section class="section">
<h2>📦 安装与引用</h2>
<div class="card">
<h3>npm</h3>
<pre><span class="cm"># 安装</span>
npm install metona-toast
<span class="cm"># ES Module</span>
<span class="kw">import</span> MeToast <span class="kw">from</span> <span class="s">'metona-toast'</span>;
<span class="cm"># CommonJS</span>
<span class="kw">const</span> MeToast = <span class="fn">require</span>(<span class="s">'metona-toast'</span>);</pre>
</div>
<div class="card">
<h3>CDN</h3>
<pre><span class="cm">&lt;!-- UMD 开发版 --&gt;</span>
<span class="kw">&lt;script</span> <span class="fn">src</span>=<span class="s">"https://unpkg.com/metona-toast@2.0.0/dist/metona-toast.js"</span><span class="kw">&gt;&lt;/script&gt;</span>
<span class="cm">&lt;!-- 压缩版(生产环境推荐)--&gt;</span>
<span class="kw">&lt;script</span> <span class="fn">src</span>=<span class="s">"https://unpkg.com/metona-toast@2.0.0/dist/metona-toast.min.js"</span><span class="kw">&gt;&lt;/script&gt;</span>
<span class="cm">&lt;!-- ES Module 直接引用 --&gt;</span>
<span class="kw">&lt;script</span> <span class="fn">type</span>=<span class="s">"module"</span><span class="kw">&gt;</span>
<span class="kw">import</span> MeToast <span class="kw">from</span> <span class="s">'https://unpkg.com/metona-toast@2.0.0/dist/metona-toast.esm.js'</span>;
<span class="kw">&lt;/script&gt;</span></pre>
</div>
</section>
<!-- 基础调用 -->
<section class="section">
<h2>🎯 基础调用</h2>
<p>五种核心类型,一行代码搞定</p>
<div class="card">
<div class="btn-row">
<button class="btn c-success" onclick="MeToast.success('操作成功!')">成功</button>
<button class="btn c-error" onclick="MeToast.error('操作失败!')">错误</button>
<button class="btn c-warning" onclick="MeToast.warning('请注意检查')">警告</button>
<button class="btn c-info" onclick="MeToast.info('系统维护中')">信息</button>
<button class="btn c-slate" onclick="MeToast.show('默认消息')">默认</button>
</div>
<pre><span class="cm">// 五种类型,一行搞定</span>
MeToast.<span class="fn">success</span>(<span class="s">'操作成功!'</span>);
MeToast.<span class="fn">error</span>(<span class="s">'操作失败!'</span>);
MeToast.<span class="fn">warning</span>(<span class="s">'请注意检查'</span>);
MeToast.<span class="fn">info</span>(<span class="s">'系统维护中'</span>);
MeToast.<span class="fn">show</span>(<span class="s">'默认消息'</span>);</pre>
</div>
</section>
<!-- 带配置 -->
<section class="section">
<h2>⚙️ 配置选项</h2>
<p>标题、位置、时长、主题、动画……灵活可控</p>
<div class="card">
<div class="btn-row">
<button class="btn c-success" onclick="MeToast.success({title:'保存成功',message:'文件已同步到云端'})">带标题</button>
<button class="btn c-pink" onclick="MeToast.info('显示10秒',{duration:10000})">长时10s</button>
<button class="btn c-purple" onclick="MeToast.warning('底部居中',{position:'bottom-center'})">底部居中</button>
<button class="btn c-teal" onclick="MeToast.info('手动关闭',{duration:0,closeButton:true})">手动关闭</button>
<button class="btn c-orange" onclick="MeToast.success('弹跳动画',{animation:'bounce'})">弹跳动画</button>
</div>
<pre><span class="cm">// 带标题</span>
MeToast.<span class="fn">success</span>({ <span class="fn">title</span>: <span class="s">'保存成功'</span>, <span class="fn">message</span>: <span class="s">'文件已同步到云端'</span> });
<span class="cm">// 自定义时长 — 显示10秒</span>
MeToast.<span class="fn">info</span>(<span class="s">'显示10秒'</span>, { <span class="fn">duration</span>: 10000 });
<span class="cm">// 自定义位置 — 底部居中</span>
MeToast.<span class="fn">warning</span>(<span class="s">'底部居中'</span>, { <span class="fn">position</span>: <span class="s">'bottom-center'</span> });
<span class="cm">// 手动关闭 — 不自动消失</span>
MeToast.<span class="fn">info</span>(<span class="s">'手动关闭'</span>, { <span class="fn">duration</span>: 0, <span class="fn">closeButton</span>: <span class="kw">true</span> });
<span class="cm">// 自定义动画 — 弹跳效果</span>
MeToast.<span class="fn">success</span>(<span class="s">'弹跳动画'</span>, { <span class="fn">animation</span>: <span class="s">'bounce'</span> });</pre>
</div>
</section>
<!-- Loading 链式 -->
<section class="section">
<h2>🔄 Loading 链式转换</h2>
<p>加载中 → 成功/失败/警告/信息,优雅切换</p>
<div class="card">
<div class="btn-row">
<button class="btn c-purple" onclick="demoLoading()">提交成功</button>
<button class="btn c-error" onclick="demoLoadingFail()">提交失败</button>
</div>
<pre><span class="cm">// loading → success</span>
<span class="kw">const</span> loading = MeToast.<span class="fn">loading</span>(<span class="s">'正在提交...'</span>);
<span class="kw">setTimeout</span>(() => loading.<span class="fn">success</span>(<span class="s">'提交成功!'</span>), 2000);
<span class="cm">// loading → error</span>
<span class="kw">const</span> loading = MeToast.<span class="fn">loading</span>(<span class="s">'正在提交...'</span>);
<span class="kw">setTimeout</span>(() => loading.<span class="fn">error</span>(<span class="s">'提交失败,请重试'</span>), 2000);</pre>
</div>
</section>
<!-- Promise -->
<section class="section">
<h2>⛓ Promise 封装</h2>
<p>一行代码自动处理 loading / success / error</p>
<div class="card">
<div class="btn-row">
<button class="btn c-primary" onclick="demoPromise()">模拟请求</button>
</div>
<pre><span class="kw">await</span> MeToast.<span class="fn">promise</span>(
<span class="fn">fetch</span>(<span class="s">'/api/data'</span>),
{
<span class="fn">loading</span>: <span class="s">'加载中...'</span>,
<span class="fn">success</span>: <span class="s">'加载完成!'</span>,
<span class="fn">error</span>: <span class="s">'加载失败'</span>,
}
);</pre>
</div>
</section>
<!-- 对话框 -->
<section class="section">
<h2>💬 确认框 & 输入框</h2>
<p>内置对话框,返回 Promise</p>
<div class="card">
<div class="btn-row">
<button class="btn c-pink" onclick="demoConfirm()">确认对话框</button>
<button class="btn c-teal" onclick="demoPrompt()">输入对话框</button>
</div>
<pre><span class="cm">// 确认对话框 → Promise&lt;boolean&gt;</span>
<span class="kw">const</span> ok = <span class="kw">await</span> MeToast.<span class="fn">confirm</span>(<span class="s">'确定删除吗?'</span>, {
<span class="fn">confirmText</span>: <span class="s">'删除'</span>,
<span class="fn">confirmColor</span>: <span class="s">'#ef4444'</span>,
});
<span class="cm">// 输入对话框 → Promise&lt;string|null&gt;</span>
<span class="kw">const</span> name = <span class="kw">await</span> MeToast.<span class="fn">prompt</span>(<span class="s">'请输入姓名'</span>, {
<span class="fn">placeholder</span>: <span class="s">'请输入...'</span>,
});</pre>
</div>
</section>
<!-- 进度 & 倒计时 -->
<section class="section">
<h2>📊 进度条 & 倒计时</h2>
<p>实时更新的进度通知和自动倒计时</p>
<div class="card">
<div class="btn-row">
<button class="btn c-orange" onclick="demoProgress()">进度条</button>
<button class="btn c-info" onclick="demoCountdown()">倒计时 5s</button>
</div>
<pre><span class="cm">// 进度条 — 实时更新</span>
<span class="kw">const</span> p = MeToast.<span class="fn">progress</span>(<span class="s">'上传中...'</span>);
<span class="kw">let</span> v = 0;
<span class="kw">const</span> t = <span class="fn">setInterval</span>(() => {
v += <span class="fn">Math</span>.random() * 28;
<span class="kw">if</span> (v >= 100) { <span class="fn">clearInterval</span>(t); p.<span class="fn">complete</span>(<span class="s">'上传完成!'</span>); }
<span class="kw">else</span> p.<span class="fn">setProgress</span>(v);
}, 400);
<span class="cm">// 倒计时 — {seconds} 自动替换</span>
MeToast.<span class="fn">countdown</span>(<span class="s">'{seconds} 秒后执行'</span>, 5, {
<span class="fn">onComplete</span>: () => MeToast.<span class="fn">success</span>(<span class="s">'已执行'</span>),
});</pre>
</div>
</section>
<!-- 队列 & 堆叠 -->
<section class="section">
<h2>📋 队列 & 堆叠</h2>
<p>顺序逐个展示 or 同时错峰展示</p>
<div class="card">
<div class="btn-row">
<button class="btn c-primary" onclick="demoQueue()">队列展示</button>
<button class="btn c-purple" onclick="demoStack()">堆叠展示</button>
</div>
<pre><span class="cm">// 队列 — 一个接一个</span>
<span class="kw">await</span> MeToast.<span class="fn">queue</span>([<span class="s">'步骤一'</span>, <span class="s">'步骤二'</span>, <span class="s">'步骤三'</span>], {
<span class="fn">delay</span>: 800,
<span class="fn">duration</span>: 2000,
<span class="fn">type</span>: <span class="s">'info'</span>,
});
<span class="cm">// 堆叠 — 同时错峰</span>
MeToast.<span class="fn">stack</span>([<span class="s">'消息 1'</span>, <span class="s">'消息 2'</span>, <span class="s">'消息 3'</span>, <span class="s">'消息 4'</span>], {
<span class="fn">stagger</span>: 150,
<span class="fn">type</span>: <span class="s">'info'</span>,
});</pre>
</div>
</section>
<!-- 插件 -->
<section class="section">
<h2>🔌 插件</h2>
<p>3 款预设插件,按需安装</p>
<div class="card">
<div class="btn-row">
<button class="btn c-purple" onclick="MeToast.use('keyboard');MeToast.info('已安装 keyboard — 试试按 ESC')">keyboard</button>
<button class="btn c-info" onclick="MeToast.use('persistence');MeToast.info('已安装 persistence — 配置将自动保存')">persistence</button>
<button class="btn c-teal" onclick="MeToast.use('accessibility');MeToast.info('已安装 accessibility — 屏幕阅读器可用')">accessibility</button>
</div>
<pre><span class="cm">// keyboard — ESC 关闭所有 Toast</span>
MeToast.<span class="fn">use</span>(<span class="s">'keyboard'</span>);
<span class="cm">// persistence — 配置持久化(自动保存/加载)</span>
MeToast.<span class="fn">use</span>(<span class="s">'persistence'</span>);
<span class="cm">// accessibility — 屏幕阅读器朗读公告</span>
MeToast.<span class="fn">use</span>(<span class="s">'accessibility'</span>);</pre>
</div>
</section>
<!-- 全局配置 -->
<section class="section">
<h2>🔧 全局配置</h2>
<p>一次配置,所有 Toast 自动继承</p>
<div class="card">
<pre>MeToast.<span class="fn">configure</span>({
<span class="fn">position</span>: <span class="s">'top-right'</span>, <span class="cm">// 默认位置</span>
<span class="fn">duration</span>: 4000, <span class="cm">// 默认显示时长(ms)</span>
<span class="fn">max</span>: 6, <span class="cm">// 同时最多显示条数</span>
<span class="fn">theme</span>: <span class="s">'auto'</span>, <span class="cm">// light | dark | auto | warm</span>
<span class="fn">animation</span>: <span class="s">'slide'</span>, <span class="cm">// 默认动画效果</span>
<span class="fn">pauseOnHover</span>: <span class="kw">true</span>, <span class="cm">// 悬停暂停计时</span>
<span class="fn">closeOnClick</span>: <span class="kw">true</span>, <span class="cm">// 点击关闭</span>
<span class="fn">showProgress</span>: <span class="kw">true</span>, <span class="cm">// 显示进度条</span>
<span class="fn">draggable</span>: <span class="kw">true</span>, <span class="cm">// 允许拖拽关闭</span>
<span class="fn">locale</span>: <span class="s">'zh-CN'</span>, <span class="cm">// 默认语言</span>
});</pre>
</div>
</section>
<!-- 主题 -->
<section class="section">
<h2>🎨 主题 & 国际化</h2>
<p>内置四种主题,支持自定义注册;内置中英文,可扩展</p>
<div class="card">
<div class="btn-row">
<button class="btn c-slate" onclick="MeToast.themes.switchTheme('light');MeToast.configure({theme:'light'});MeToast.success('浅色主题')">浅色</button>
<button class="btn c-slate" onclick="MeToast.themes.switchTheme('dark');MeToast.configure({theme:'dark'});MeToast.success('深色主题')">深色</button>
<button class="btn c-slate" onclick="MeToast.themes.switchTheme('warm');MeToast.configure({theme:'warm'});MeToast.success('暖色主题')">暖色</button>
<button class="btn c-info" onclick="MeToast.i18n.switchLocale('en-US');MeToast.success('Switched to English')">EN</button>
<button class="btn c-info" onclick="MeToast.i18n.switchLocale('zh-CN');MeToast.success('已切换为中文')">中文</button>
</div>
<pre><span class="cm">// 切换主题(switchTheme + configure 同步全局配置)</span>
MeToast.themes.<span class="fn">switchTheme</span>(<span class="s">'warm'</span>);
MeToast.<span class="fn">configure</span>({ <span class="fn">theme</span>: <span class="s">'warm'</span> });
<span class="cm">// 切换语言 — 英文</span>
MeToast.i18n.<span class="fn">switchLocale</span>(<span class="s">'en-US'</span>);
<span class="cm">// 切换回中文</span>
MeToast.i18n.<span class="fn">switchLocale</span>(<span class="s">'zh-CN'</span>);
<span class="cm">// 注册自定义主题</span>
MeToast.themes.<span class="fn">registerTheme</span>(<span class="s">'ocean'</span>, {
<span class="fn">bg</span>: <span class="s">'rgba(240,249,255,0.96)'</span>,
<span class="fn">text</span>: <span class="s">'#0c4a6e'</span>,
<span class="fn">border</span>: <span class="s">'rgba(14,165,233,0.2)'</span>,
<span class="fn">shadow</span>: <span class="s">'0 10px 36px -10px rgba(14,165,233,0.18)'</span>,
<span class="fn">hoverShadow</span>: <span class="s">'0 14px 48px -10px rgba(14,165,233,0.22)'</span>,
<span class="fn">progressBg</span>: <span class="s">'rgba(14,165,233,0.1)'</span>,
<span class="fn">closeHoverBg</span>: <span class="s">'rgba(14,165,233,0.1)'</span>,
});</pre>
</div>
</section>
<!-- Toast 管理 -->
<section class="section">
<h2>🔧 Toast 管理</h2>
<p>查找、关闭、清除、暂停、恢复</p>
<div class="card">
<div class="btn-row">
<button class="btn c-success" onclick="MeToast.success('消息 '+Date.now()%100)">发一条</button>
<button class="btn c-info" onclick="MeToast.info('消息 '+Date.now()%100)">再发一条</button>
<button class="btn c-warning" onclick="alert('当前: '+MeToast.count()+' 条')">查看数量</button>
<button class="btn c-slate" onclick="MeToast.pauseAll();MeToast.info('已暂停全部计时')">暂停全部</button>
<button class="btn c-slate" onclick="MeToast.resumeAll();MeToast.info('已恢复全部计时')">恢复全部</button>
<button class="btn c-error" onclick="MeToast.dismiss()">关闭全部</button>
</div>
<pre>MeToast.<span class="fn">count</span>(); <span class="cm">// 当前数量</span>
MeToast.<span class="fn">getToasts</span>(); <span class="cm">// 获取所有实例</span>
MeToast.<span class="fn">dismiss</span>(); <span class="cm">// 关闭全部</span>
MeToast.<span class="fn">dismiss</span>(id); <span class="cm">// 关闭指定</span>
MeToast.<span class="fn">pauseAll</span>(); <span class="cm">// 暂停全部计时</span>
MeToast.<span class="fn">resumeAll</span>(); <span class="cm">// 恢复全部计时</span>
MeToast.<span class="fn">destroy</span>(); <span class="cm">// 完全销毁</span></pre>
</div>
</section>
<!-- API 速览 -->
<section class="section">
<h2>📖 API 速览</h2>
<div class="card">
<table class="api-table">
<tr><th>方法</th><th>说明</th><th>示例</th></tr>
<tr><td><code>success(msg, opts?)</code></td><td>成功通知</td><td><code>MeToast.success('保存成功')</code></td></tr>
<tr><td><code>error(msg, opts?)</code></td><td>错误通知</td><td><code>MeToast.error('网络错误')</code></td></tr>
<tr><td><code>warning(msg, opts?)</code></td><td>警告通知</td><td><code>MeToast.warning('请注意')</code></td></tr>
<tr><td><code>info(msg, opts?)</code></td><td>信息通知</td><td><code>MeToast.info('系统维护中')</code></td></tr>
<tr><td><code>loading(msg, opts?)</code></td><td>加载状态</td><td><code>MeToast.loading('加载中...')</code></td></tr>
<tr><td><code>promise(p, opts)</code></td><td>Promise 自动状态</td><td><code>MeToast.promise(fetch(...), opts)</code></td></tr>
<tr><td><code>confirm(msg, opts?)</code></td><td>确认对话框 → boolean</td><td><code>await MeToast.confirm('删除?')</code></td></tr>
<tr><td><code>prompt(msg, opts?)</code></td><td>输入对话框 → string</td><td><code>await MeToast.prompt('姓名?')</code></td></tr>
<tr><td><code>progress(msg, opts?)</code></td><td>进度条通知</td><td><code>MeToast.progress('上传中...')</code></td></tr>
<tr><td><code>countdown(msg, s, opts?)</code></td><td>倒计时</td><td><code>MeToast.countdown('{seconds}s', 10)</code></td></tr>
<tr><td><code>queue(msgs, opts?)</code></td><td>队列顺序展示</td><td><code>MeToast.queue(['a','b','c'])</code></td></tr>
<tr><td><code>stack(msgs, opts?)</code></td><td>堆叠错峰展示</td><td><code>MeToast.stack(['a','b','c'])</code></td></tr>
<tr><td><code>configure(opts)</code></td><td>全局配置</td><td><code>MeToast.configure({duration:5000})</code></td></tr>
<tr><td><code>dismiss(id?)</code></td><td>关闭 toast</td><td><code>MeToast.dismiss()</code></td></tr>
</table>
</div>
</section>
</main>
<footer>
<p>MetonaToast v2.0.0 · <a href="demo.html">全功能演示 →</a></p>
</footer>
<script src="../dist/metona-toast.js"></script>
<script>
MeToast.configure({ position: 'top-right', duration: 4000, theme: 'dark', animation: 'slide', pauseOnHover: true, showProgress: true });
function demoLoading() {
const l = MeToast.loading('正在提交...');
setTimeout(() => l.success('提交成功!'), 2000);
}
function demoLoadingFail() {
const l = MeToast.loading('正在提交...');
setTimeout(() => l.error('提交失败,请重试'), 2000);
}
async function demoPromise() {
const p = new Promise((resolve, reject) => {
setTimeout(() => Math.random() > 0.3 ? resolve('ok') : reject('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('已删除'); else MeToast.info('已取消');
}
async function demoPrompt() {
const name = await MeToast.prompt('请输入姓名', { placeholder: '请输入...' });
if (name) MeToast.info(`你好,${name}`);
}
function demoProgress() {
const p = MeToast.progress('上传中...');
let v = 0;
const t = setInterval(() => { v += Math.random() * 28; if (v >= 100) { clearInterval(t); p.complete('上传完成!'); } else p.setProgress(v); }, 400);
}
function demoCountdown() {
MeToast.countdown('{seconds} 秒后执行', 5, { onComplete: () => MeToast.success('已执行') });
}
async function demoQueue() {
await MeToast.queue(['步骤一', '步骤二', '步骤三'], { delay: 800, duration: 2000, type: 'info' });
}
function demoStack() {
MeToast.stack(['消息 1', '消息 2', '消息 3', '消息 4'], { stagger: 150, type: 'info' });
}
</script>
</body>
</html>
+2 -2
View File
@@ -21,11 +21,11 @@ const baseConfig = {
const devPlugins = isDev ? [
serve({
open: true,
contentBase: ['examples', 'dist'],
contentBase: ['site', 'dist'],
port: 3000,
}),
livereload({
watch: ['src', 'examples'],
watch: ['src', 'site'],
}),
] : [];
+3 -2
View File
@@ -21,8 +21,9 @@ echo "✅ Python: $($PYTHON --version 2>&1)"
echo "🌐 启动服务器..."
echo ""
echo "📁 访问地址:"
echo " http://localhost:$PORT/examples/demo.html"
echo " http://localhost:$PORT/examples/index.html"
echo " http://localhost:$PORT/site/index.html"
echo " http://localhost:$PORT/site/demo.html"
echo " http://localhost:$PORT/site/docs.html"
echo ""
echo "按 Ctrl+C 停止服务器"
echo ""
+259
View File
@@ -0,0 +1,259 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在线演示 — MetonaToast</title>
<style>
:root {
--bg: #0b1121; --surface: #151d33; --border: #1e2d4d;
--text: #e2e8f0; --muted: #64748b; --accent: #6366f1; --accent2: #818cf8;
--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.6; min-height:100vh; }
a { color:var(--accent2); text-decoration:none; }
code { background:rgba(99,102,241,0.12); color:#a5b4fc; padding:1px 6px; border-radius:4px; font-size:0.85em; }
.nav { position:sticky; top:0; z-index:100; background:rgba(11,17,33,0.88); backdrop-filter:blur(14px); border-bottom:1px solid var(--border); }
.nav-inner { max-width:1300px; margin:0 auto; display:flex; align-items:center; justify-content:space-between; padding:0 20px; height:56px; }
.nav-logo { font-size:18px; font-weight:700; background:linear-gradient(135deg,#818cf8,#c084fc,#f472b6); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.nav-links { display:flex; gap:20px; }
.nav-links a { color:var(--muted); font-size:13px; font-weight:500; transition:.2s; }
.nav-links a:hover { color:var(--accent2); }
.hero { text-align:center; padding:50px 20px 40px; border-bottom:1px solid var(--border); background:radial-gradient(ellipse at 50% 0%,rgba(99,102,241,0.12) 0%,transparent 50%); }
.hero h1 { font-size:36px; font-weight:800; }
.hero h1 span { background:linear-gradient(135deg,#818cf8,#c084fc,#f472b6); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.hero p { color:var(--muted); font-size:14px; margin-top:6px; }
.container { max-width:1300px; margin:0 auto; padding:32px 20px; display:grid; grid-template-columns:repeat(auto-fill,minmax(380px,1fr)); gap:20px; }
.card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:22px; }
.card h2 { font-size:16px; margin-bottom:4px; }
.card > p { color:var(--muted); font-size:12px; margin-bottom:14px; }
.btn-row { display:flex; flex-wrap:wrap; gap:7px; }
.btn { padding:8px 15px; border:none; border-radius:7px; font-size:12px; font-weight:500; cursor:pointer; transition:.15s; color:#fff; }
.btn:hover { transform:translateY(-1px); filter:brightness(1.15); }
.btn-sm { padding:5px 10px; font-size:11px; border-radius:5px; }
.btn-outline { background:transparent; border:1px solid var(--border); color:var(--text); }
.btn-outline:hover { border-color:var(--accent); background:rgba(99,102,241,0.06); }
.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; }
.label { font-size:10px; text-transform:uppercase; letter-spacing:0.8px; color:var(--muted); margin-bottom:12px; font-weight:600; }
.demo-section { border-top:1px solid var(--border); padding-top:10px; margin-top:10px; }
footer { text-align:center; padding:30px 20px; color:var(--muted); font-size:12px; border-top:1px solid var(--border); }
@media (max-width:768px) { .container { grid-template-columns:1fr; } }
</style>
</head>
<body>
<nav class="nav">
<div class="nav-inner">
<div class="nav-logo">MetonaToast</div>
<div class="nav-links">
<a href="index.html">首页</a>
<a href="docs.html">文档</a>
<a href="demo.html">演示</a>
<a href="https://github.com/metona/metona-toast" target="_blank">GitHub</a>
</div>
</div>
</nav>
<header class="hero">
<h1><span>在线演示</span></h1>
<p>所有功能均可点击体验 — 192 个测试用例全部通过验证</p>
</header>
<main class="container">
<!-- 基础通知 -->
<div class="card">
<div class="label">📢 基础通知</div>
<div class="btn-row">
<button class="btn c-s" onclick="M.success('操作成功!')">✓ 成功</button>
<button class="btn c-e" onclick="M.error('操作失败!')">✕ 错误</button>
<button class="btn c-w" onclick="M.warning('请注意检查')">⚠ 警告</button>
<button class="btn c-i" onclick="M.info('系统维护中')"> 信息</button>
<button class="btn c-sl" onclick="M.show('默认消息')">— 默认</button>
</div>
<div class="demo-section">
<div class="btn-row">
<button class="btn btn-outline btn-sm" onclick="M.success({title:'保存成功',message:'文件已同步到云端'})">带标题</button>
<button class="btn btn-outline btn-sm" onclick="M.info('显示10秒',{duration:10000})">长时10s</button>
<button class="btn btn-outline btn-sm" onclick="M.info('手动关闭',{duration:0})">手动关闭</button>
<button class="btn btn-outline btn-sm" onclick="M.info('隐藏图标',{icon:false})">无图标</button>
<button class="btn btn-outline btn-sm" onclick="M.info('隐藏进度条',{showProgress:false})">无进度条</button>
</div>
</div>
</div>
<!-- Loading 链式 -->
<div class="card">
<div class="label">🔄 Loading 链式转换</div>
<div class="btn-row">
<button class="btn c-p" onclick="demoLoading()">提交成功</button>
<button class="btn c-e" onclick="demoLoadingFail()">提交失败</button>
<button class="btn c-pp" onclick="demoPromise()">Promise 风格</button>
</div>
</div>
<!-- 对话框 -->
<div class="card">
<div class="label">💬 对话框</div>
<div class="btn-row">
<button class="btn c-pk" onclick="demoConfirm()">确认对话框</button>
<button class="btn c-t" onclick="demoPrompt()">输入对话框</button>
<button class="btn c-o" onclick="demoCountdown()">倒计时 5s</button>
</div>
</div>
<!-- 进度 & 队列 -->
<div class="card">
<div class="label">📊 进度 & 队列</div>
<div class="btn-row">
<button class="btn c-o" onclick="demoProgress()">进度条</button>
<button class="btn c-p" onclick="demoQueue()">队列展示</button>
<button class="btn c-pp" onclick="demoStack()">堆叠展示</button>
</div>
</div>
<!-- Action Toast -->
<div class="card">
<div class="label">⚡ Action Toast <span style="color:#34d399;font-size:10px;">NEW</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="demoActionSuccess()">成功+查看</button>
</div>
</div>
<!-- 分组管理 -->
<div class="card">
<div class="label">📁 分组管理 <span style="color:#34d399;font-size:10px;">NEW</span></div>
<div class="btn-row">
<button class="btn c-p" onclick="demoGroup()">创建分组</button>
<button class="btn c-e" onclick="dismissGroup()">关闭分组</button>
<button class="btn btn-outline btn-sm" onclick="M.info('分组数量: '+group.count())">查看数量</button>
</div>
</div>
<!-- 动画效果 -->
<div class="card">
<div class="label">🎭 动画效果</div>
<div class="btn-row">
<button class="btn btn-outline btn-sm" onclick="anim('slide')">slide</button>
<button class="btn btn-outline btn-sm" onclick="anim('fade')">fade</button>
<button class="btn btn-outline btn-sm" onclick="anim('scale')">scale</button>
<button class="btn btn-outline btn-sm" onclick="anim('bounce')">bounce</button>
<button class="btn btn-outline btn-sm" onclick="anim('flip')">flip</button>
<button class="btn btn-outline btn-sm" onclick="anim('rotate')">rotate</button>
<button class="btn btn-outline btn-sm" onclick="anim('zoom')">zoom</button>
<button class="btn btn-outline btn-sm" onclick="anim('slideUp')">slideUp</button>
<button class="btn btn-outline btn-sm" onclick="anim('slideDown')">slideDown</button>
<button class="btn btn-outline btn-sm" onclick="anim('slideLeft')">slideLeft</button>
<button class="btn btn-outline btn-sm" onclick="anim('slideRight')">slideRight</button>
</div>
</div>
<!-- 位置 -->
<div class="card">
<div class="label">📍 位置</div>
<div class="btn-row">
<button class="btn c-sl" onclick="M.success('左上角',{position:'top-left'})">↖ top-left</button>
<button class="btn c-sl" onclick="M.success('顶部居中',{position:'top-center'})">↑ top-center</button>
<button class="btn c-sl" onclick="M.success('右上角',{position:'top-right'})">↗ top-right</button>
<button class="btn c-sl" onclick="M.success('左下角',{position:'bottom-left'})">↙ bottom-left</button>
<button class="btn c-sl" onclick="M.success('底部居中',{position:'bottom-center'})">↓ bottom-center</button>
<button class="btn c-sl" onclick="M.success('右下角',{position:'bottom-right'})">↘ bottom-right</button>
</div>
</div>
<!-- 主题 -->
<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>
</div>
</div>
<!-- 插件 -->
<div class="card">
<div class="label">🔌 插件</div>
<div class="btn-row">
<button class="btn c-p" onclick="M.use('keyboard');M.info('已安装 — 试试按 ESC 键','keyboard插件')">keyboard ESC关闭</button>
<button class="btn c-i" onclick="M.use('persistence');M.info('已安装 — 配置将自动保存','persistence插件')">persistence 持久化</button>
<button class="btn c-t" onclick="M.use('accessibility');M.info('已安装 — 屏幕阅读器可用','accessibility插件')">accessibility 无障碍</button>
</div>
</div>
<!-- 管理 -->
<div class="card">
<div class="label">🔧 Toast 管理</div>
<div class="btn-row">
<button class="btn c-s" onclick="M.success('消息 '+(M.count()+1))">发一条</button>
<button class="btn c-i" onclick="M.info('消息 '+(M.count()+1))">再发一条</button>
<button class="btn c-w" onclick="alert('当前共 '+M.count()+' 条消息')">查看数量</button>
<button class="btn c-e" onclick="M.dismiss()">关闭全部</button>
<button class="btn c-sl" onclick="M.pauseAll();M.info('已暂停全部计时')">暂停全部</button>
<button class="btn c-sl" onclick="M.resumeAll();M.info('已恢复全部计时')">恢复全部</button>
</div>
</div>
</main>
<footer>
MetonaToast v2.0.0 · MIT License · <a href="index.html">首页</a> · <a href="docs.html">文档</a>
</footer>
<script src="../dist/metona-toast.js"></script>
<script>
const M = MeToast;
M.configure({ position:'top-right', duration:4000, theme:'dark', animation:'slide', pauseOnHover:true, showProgress:true });
// Loading
function demoLoading() { const l=M.loading('正在提交...'); setTimeout(()=>l.success('提交成功!'),2000); }
function demoLoadingFail() { const l=M.loading('正在提交...'); setTimeout(()=>l.error('提交失败,请重试'),2000); }
async function demoPromise() {
const p=new Promise((r,e)=>setTimeout(()=>Math.random()>0.3?r('ok'):e('fail'),2000));
try{await M.promise(p,{loading:'处理中...',success:'成功!',error:'失败'});}catch(_){}
}
// 对话框
async function demoConfirm() { const ok=await M.confirm('确定删除吗?',{confirmText:'删除',confirmColor:'#ef4444'}); if(ok)M.success('已删除'); else M.info('已取消'); }
async function demoPrompt() { const v=await M.prompt('请输入姓名',{placeholder:'请输入...'}); if(v) M.info('你好,'+v+''); }
// 进度 & 队列
function demoProgress() { const p=M.progress('上传中...'); let v=0; const t=setInterval(()=>{v+=Math.random()*28; if(v>=100){clearInterval(t);p.complete('完成!');}else p.setProgress(v);},400); }
function demoCountdown() { M.countdown('{seconds} 秒后执行',5,{onComplete:()=>M.success('已执行')}); }
async function demoQueue() { await M.queue(['步骤一','步骤二','步骤三'],{delay:800,duration:2000,type:'info'}); }
function demoStack() { M.stack(['消息1','消息2','消息3','消息4'],{stagger:150,type:'info'}); }
// Action
function demoActionDelete() {
let deleted=true;
M.action('文件 data.json 已删除',[{text:'撤销',onClick:()=>{deleted=false;M.success('已撤销删除');},color:'#3b82f6'}]);
}
function demoActionMulti() {
M.action('邮件已发送',[
{text:'查看',onClick:()=>M.info('打开邮件详情'),color:'#3b82f6'},
{text:'撤回',onClick:()=>M.warning('邮件已撤回'),color:'#f59e0b'},
]);
}
function demoActionSuccess() {
M.action('上传完成',[{text:'查看文件',onClick:()=>M.info('打开文件管理器'),color:'#10b981'}]);
}
// 分组
const group = M.group('demo-group');
function demoGroup() { group.success('分组消息 #'+(group.count()+1)); group.info('分组消息 #'+(group.count()+1)); }
function dismissGroup() { M.dismissGroup('demo-group'); M.info('已关闭 demo-group 分组'); }
// 动画
function anim(name) { M.success('动画: '+name,{animation:name,duration:3000}); }
function switchTheme(t) { M.themes.switchTheme(t); M.configure({theme:t}); const n={light:'浅色',dark:'深色',auto:'自动',warm:'暖色'}; M.info('主题: '+n[t],{theme:t}); }
</script>
</body>
</html>
+355
View File
@@ -0,0 +1,355 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API 文档 — MetonaToast</title>
<style>
:root {
--bg: #0b1121; --surface: #1a2332; --border: #1e2d4d;
--text: #e2e8f0; --muted: #64748b; --accent: #6366f1; --accent2: #818cf8;
--radius: 10px;
}
* { 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.7; display:flex; min-height:100vh; }
code { background:rgba(99,102,241,0.12); color:#a5b4fc; padding:1px 6px; border-radius:4px; font-size:0.9em; white-space:nowrap; }
pre { background:#0a0f1a; border:1px solid var(--border); border-radius:8px; padding:14px 18px; overflow-x:auto; font:13px/1.7 'SF Mono','Cascadia Code','Fira Code',Consolas,monospace; color:#cbd5e1; margin:10px 0; }
pre .kw { color:#c084fc; } pre .fn { color:#60a5fa; } pre .s { color:#34d399; } pre .cm { color:#475569; }
a { color:var(--accent2); text-decoration:none; }
.sidebar { width:260px; min-width:260px; background:var(--surface); border-right:1px solid var(--border); padding:24px 0; position:sticky; top:0; height:100vh; overflow-y:auto; }
.sidebar h2 { font-size:15px; padding:0 24px; margin-bottom:16px; color:var(--accent2); }
.sidebar a { display:block; padding:7px 24px; font-size:13px; color:var(--muted); transition:.15s; border-left:2px solid transparent; }
.sidebar a:hover, .sidebar a.active { color:var(--accent2); border-left-color:var(--accent); background:rgba(99,102,241,0.06); }
.sidebar .section { font-size:11px; text-transform:uppercase; letter-spacing:1px; padding:16px 24px 6px; color:var(--muted); font-weight:600; }
.main { flex:1; padding:40px 48px; max-width:900px; }
.main h1 { font-size:30px; font-weight:700; margin-bottom:8px; }
.main h2 { font-size:22px; font-weight:700; margin:40px 0 12px; padding-top:20px; border-top:1px solid var(--border); }
.main h3 { font-size:17px; font-weight:600; margin:24px 0 8px; color:var(--accent2); }
.main p { margin:8px 0; color:var(--muted); font-size:14px; }
.main table { width:100%; border-collapse:collapse; margin:12px 0; font-size:13px; }
.main th, .main td { text-align:left; padding:8px 14px; border-bottom:1px solid var(--border); }
.main th { color:var(--muted); font-weight:500; font-size:11px; text-transform:uppercase; letter-spacing:0.5px; }
.main td:first-child { color:var(--accent2); font-family:monospace; font-size:12px; white-space:nowrap; }
.tag { display:inline-block; padding:2px 8px; border-radius:4px; font-size:11px; font-weight:600; }
.tag-method { background:rgba(59,130,246,0.15); color:#60a5fa; }
.tag-config { background:rgba(16,185,129,0.15); color:#34d399; }
.tag-callback { background:rgba(139,92,246,0.15); color:#a78bfa; }
.badge { display:inline-block; padding:1px 7px; border-radius:3px; font-size:10px; font-weight:700; margin-left:6px; vertical-align:middle; }
.badge-new { background:rgba(16,185,129,0.2); color:#34d399; }
.badge-v2 { background:rgba(99,102,241,0.2); color:#818cf8; }
@media (max-width:768px) {
body { flex-direction:column; }
.sidebar { width:100%; min-width:0; height:auto; position:static; border-right:none; border-bottom:1px solid var(--border); padding:12px 16px; }
.sidebar a { display:inline-block; padding:4px 10px; border-left:none; }
.main { padding:24px 20px; }
}
</style>
</head>
<body>
<nav class="sidebar">
<h2>📘 MetonaToast 文档</h2>
<a href="index.html">← 返回首页</a>
<a href="demo.html">🎮 在线演示</a>
<div class="section">基础通知</div>
<a href="#show">show()</a>
<a href="#success">success()</a>
<a href="#error">error()</a>
<a href="#warning">warning()</a>
<a href="#info">info()</a>
<a href="#loading">loading()</a>
<div class="section">高级功能</div>
<a href="#promise">promise()</a>
<a href="#confirm">confirm()</a>
<a href="#prompt">prompt()</a>
<a href="#progress">progress()</a>
<a href="#countdown">countdown()</a>
<a href="#queue">queue()</a>
<a href="#stack">stack()</a>
<a href="#action">action() <span class="badge badge-new">NEW</span></a>
<div class="section">管理方法</div>
<a href="#group">group() <span class="badge badge-new">NEW</span></a>
<a href="#dismiss">dismiss()</a>
<a href="#configure">configure()</a>
<a href="#use">use()</a>
<a href="#destroy">destroy()</a>
<div class="section">配置参考</div>
<a href="#config">全部配置项</a>
<a href="#callbacks">回调函数</a>
<a href="#animations">动画列表</a>
<a href="#themes">主题参考</a>
</nav>
<main class="main">
<h1>API 文档</h1>
<p>MetonaToast v2.0.0 完整 API 参考。所有方法均支持链式调用和配置覆盖。</p>
<!-- ===== 基础通知 ===== -->
<h2 id="show">show(message, opts?)</h2>
<p>显示默认类型的 Toast 通知。</p>
<pre><span class="cm">// 字符串</span>
MeToast.<span class="fn">show</span>(<span class="s">'默认消息'</span>);
<span class="cm">// 带配置</span>
MeToast.<span class="fn">show</span>(<span class="s">'自定义'</span>, { <span class="fn">duration</span>: 2000, <span class="fn">position</span>: <span class="s">'bottom-center'</span> });
<span class="cm">// 对象形式</span>
MeToast.<span class="fn">show</span>({ <span class="fn">title</span>: <span class="s">'标题'</span>, <span class="fn">message</span>: <span class="s">'内容'</span> });</pre>
<h2 id="success">success(message, opts?)</h2>
<p>显示成功通知。绿色对勾图标。</p>
<pre>MeToast.<span class="fn">success</span>(<span class="s">'保存成功!'</span>);
MeToast.<span class="fn">success</span>({ <span class="fn">title</span>: <span class="s">'已保存'</span>, <span class="fn">message</span>: <span class="s">'文件已同步到云端'</span> });</pre>
<h2 id="error">error(message, opts?)</h2>
<p>显示错误通知。红色叉号图标。</p>
<pre>MeToast.<span class="fn">error</span>(<span class="s">'网络错误,请重试'</span>);</pre>
<h2 id="warning">warning(message, opts?)</h2>
<p>显示警告通知。黄色三角图标。</p>
<pre>MeToast.<span class="fn">warning</span>(<span class="s">'请注意检查输入内容'</span>);</pre>
<h2 id="info">info(message, opts?)</h2>
<p>显示信息通知。蓝色圆形图标。</p>
<pre>MeToast.<span class="fn">info</span>(<span class="s">'系统将于 22:00 进行维护'</span>);</pre>
<h2 id="loading">loading(message, opts?)</h2>
<p>显示加载状态。返回一个控制对象,支持链式转换为 success/error/info/warning。</p>
<pre><span class="kw">const</span> loading = MeToast.<span class="fn">loading</span>(<span class="s">'正在提交...'</span>);
<span class="fn">setTimeout</span>(() => loading.<span class="fn">success</span>(<span class="s">'提交成功!'</span>), 2000);
<span class="cm">// 返回对象方法</span>
loading.<span class="fn">success</span>(msg, opts?) <span class="cm">// 转为成功</span>
loading.<span class="fn">error</span>(msg, opts?) <span class="cm">// 转为错误</span>
loading.<span class="fn">info</span>(msg, opts?) <span class="cm">// 转为信息</span>
loading.<span class="fn">warning</span>(msg, opts?) <span class="cm">// 转为警告</span>
loading.<span class="fn">update</span>(partial) <span class="cm">// 原地更新</span>
loading.<span class="fn">dismiss</span>() <span class="cm">// 关闭</span></pre>
<!-- ===== 高级功能 ===== -->
<h2 id="promise">promise(promise, opts)</h2>
<p>自动根据 Promise 状态切换 Toast。显示 loading → 自动 success/error。</p>
<pre><span class="kw">await</span> MeToast.<span class="fn">promise</span>(
<span class="fn">fetch</span>(<span class="s">'/api/data'</span>),
{
<span class="fn">loading</span>: <span class="s">'加载中...'</span>,
<span class="fn">success</span>: <span class="s">'加载完成!'</span>,
<span class="fn">error</span>: <span class="s">'加载失败'</span>,
}
);</pre>
<table>
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
<tr><td>promise</td><td>Promise</td><td>要监听的 Promise 对象</td></tr>
<tr><td>opts.loading</td><td>string</td><td>加载中文本</td></tr>
<tr><td>opts.success</td><td>string</td><td>成功时文本</td></tr>
<tr><td>opts.error</td><td>string</td><td>失败时文本</td></tr>
</table>
<h2 id="confirm">confirm(message, opts?)</h2>
<p>确认对话框。返回 <code>Promise&lt;boolean&gt;</code>,用户点击确认返回 true。</p>
<pre><span class="kw">const</span> ok = <span class="kw">await</span> MeToast.<span class="fn">confirm</span>(<span class="s">'确定删除吗?'</span>, {
<span class="fn">confirmText</span>: <span class="s">'删除'</span>,
<span class="fn">confirmColor</span>: <span class="s">'#ef4444'</span>,
<span class="fn">cancelText</span>: <span class="s">'取消'</span>,
});
<span class="kw">if</span> (ok) MeToast.<span class="fn">success</span>(<span class="s">'已删除'</span>);</pre>
<h2 id="prompt">prompt(message, opts?)</h2>
<p>输入对话框。返回 <code>Promise&lt;string|null&gt;</code>,用户取消返回 null。</p>
<pre><span class="kw">const</span> name = <span class="kw">await</span> MeToast.<span class="fn">prompt</span>(<span class="s">'请输入姓名'</span>, {
<span class="fn">placeholder</span>: <span class="s">'请输入...'</span>,
<span class="fn">defaultValue</span>: <span class="s">'张三'</span>,
});
<span class="kw">if</span> (name) MeToast.<span class="fn">info</span>(<span class="s">'你好,'</span> + name + <span class="s">''</span>);</pre>
<h2 id="progress">progress(message, opts?)</h2>
<p>进度条通知。返回控制对象,可调用 setProgress / complete / error。</p>
<pre><span class="kw">const</span> p = MeToast.<span class="fn">progress</span>(<span class="s">'上传中...'</span>);
p.<span class="fn">setProgress</span>(30);
p.<span class="fn">setProgress</span>(70);
p.<span class="fn">complete</span>(<span class="s">'上传完成!'</span>);
<span class="cm">// 或</span>
p.<span class="fn">error</span>(<span class="s">'上传失败'</span>);
p.<span class="fn">dismiss</span>();</pre>
<h2 id="countdown">countdown(message, seconds, opts?)</h2>
<p>倒计时 Toast。<code>{seconds}</code> 占位符自动替换为剩余秒数。</p>
<pre>MeToast.<span class="fn">countdown</span>(<span class="s">'操作将在 {seconds} 秒后执行'</span>, 5, {
<span class="fn">onComplete</span>: () => MeToast.<span class="fn">success</span>(<span class="s">'已执行'</span>),
});
<span class="cm">// 返回控制对象</span>
<span class="kw">const</span> c = MeToast.<span class="fn">countdown</span>(<span class="s">'{seconds}s'</span>, 10);
c.<span class="fn">cancel</span>(); <span class="cm">// 取消并关闭</span>
c.<span class="fn">pause</span>(); <span class="cm">// 暂停</span>
c.<span class="fn">resume</span>(); <span class="cm">// 恢复</span></pre>
<h2 id="queue">queue(messages, opts?)</h2>
<p>队列展示:顺序逐个显示消息。返回可取消的 thenable 对象。</p>
<pre><span class="kw">const</span> q = MeToast.<span class="fn">queue</span>([<span class="s">'步骤一'</span>, <span class="s">'步骤二'</span>, <span class="s">'步骤三'</span>], {
<span class="fn">delay</span>: 800,
<span class="fn">duration</span>: 2000,
<span class="fn">type</span>: <span class="s">'info'</span>,
});
q.<span class="fn">cancel</span>(); <span class="cm">// 取消队列</span>
<span class="kw">await</span> q; <span class="cm">// 等待队列完成</span></pre>
<h2 id="stack">stack(messages, opts?)</h2>
<p>堆叠展示:同时错峰显示多条消息。</p>
<pre>MeToast.<span class="fn">stack</span>([<span class="s">'消息1'</span>, <span class="s">'消息2'</span>, <span class="s">'消息3'</span>, <span class="s">'消息4'</span>], {
<span class="fn">stagger</span>: 150,
<span class="fn">type</span>: <span class="s">'info'</span>,
});</pre>
<h2 id="action">action(message, actions, opts?) <span class="badge badge-new">v2.0</span></h2>
<p>Action Toast:内嵌操作按钮的通知。类似 Android Snackbar。</p>
<pre>MeToast.<span class="fn">action</span>(<span class="s">'文件已删除'</span>, [
{ <span class="fn">text</span>: <span class="s">'撤销'</span>, <span class="fn">onClick</span>: () => <span class="fn">restore</span>(), <span class="fn">color</span>: <span class="s">'#3b82f6'</span> },
{ <span class="fn">text</span>: <span class="s">'查看'</span>, <span class="fn">onClick</span>: () => <span class="fn">openFile</span>() },
]);</pre>
<table>
<tr><th>按钮字段</th><th>类型</th><th>说明</th></tr>
<tr><td>text</td><td>string</td><td>按钮文本</td></tr>
<tr><td>onClick</td><td>function</td><td>点击回调,参数为 toast 实例</td></tr>
<tr><td>color</td><td>string</td><td>按钮背景色,默认 #6366f1</td></tr>
<tr><td>close</td><td>boolean</td><td>点击后是否关闭 toast,默认 true</td></tr>
</table>
<!-- ===== 管理方法 ===== -->
<h2 id="group">group(name) <span class="badge badge-new">v2.0</span></h2>
<p>创建 Toast 分组。返回一个自动添加 <code>group</code> 参数的子对象,支持按组管理。</p>
<pre><span class="kw">const</span> orders = MeToast.<span class="fn">group</span>(<span class="s">'orders'</span>);
orders.<span class="fn">success</span>(<span class="s">'订单已创建'</span>);
orders.<span class="fn">error</span>(<span class="s">'支付失败'</span>);
orders.<span class="fn">count</span>(); <span class="cm">// 该组的 toast 数量</span>
orders.<span class="fn">dismiss</span>(); <span class="cm">// 关闭整组</span>
<span class="cm">// 也可通过主对象关闭</span>
MeToast.<span class="fn">dismissGroup</span>(<span class="s">'orders'</span>);</pre>
<h2 id="dismiss">dismiss(id?)</h2>
<p>关闭 Toast。无参数关闭所有,传 id 关闭指定。</p>
<pre>MeToast.<span class="fn">dismiss</span>(); <span class="cm">// 关闭所有</span>
MeToast.<span class="fn">dismiss</span>(toastId); <span class="cm">// 关闭指定</span></pre>
<p>其他管理方法:<code>count()</code> <code>getToasts()</code> <code>pauseAll()</code> <code>resumeAll()</code> <code>destroy()</code> <code>clear()</code></p>
<h2 id="configure">configure(opts)</h2>
<p>全局配置。影响后续所有 Toast。</p>
<pre>MeToast.<span class="fn">configure</span>({
<span class="fn">position</span>: <span class="s">'top-right'</span>,
<span class="fn">duration</span>: 4000,
<span class="fn">theme</span>: <span class="s">'dark'</span>,
<span class="fn">animation</span>: <span class="s">'slide'</span>,
<span class="fn">locale</span>: <span class="s">'zh-CN'</span>,
});</pre>
<h2 id="use">use(plugin)</h2>
<p>安装插件。支持字符串(预设)和对象(自定义)。</p>
<pre>MeToast.<span class="fn">use</span>(<span class="s">'keyboard'</span>); <span class="cm">// ESC 关闭所有 Toast</span>
MeToast.<span class="fn">use</span>(<span class="s">'persistence'</span>); <span class="cm">// 配置持久化到 localStorage</span>
MeToast.<span class="fn">use</span>(<span class="s">'accessibility'</span>); <span class="cm">// 屏幕阅读器朗读</span>
<span class="cm">// 自定义插件</span>
MeToast.<span class="fn">use</span>({
<span class="fn">name</span>: <span class="s">'my-plugin'</span>,
<span class="fn">install</span>() {
Toast.<span class="fn">on</span>(<span class="s">'afterShow'</span>, (t) => <span class="fn">console</span>.log(t.message));
},
});</pre>
<h2 id="destroy">destroy()</h2>
<p>完全销毁:关闭所有 Toast、移除 DOM 容器和样式标签。</p>
<pre>MeToast.<span class="fn">destroy</span>();</pre>
<!-- ===== 配置参考 ===== -->
<h2 id="config">全部配置项</h2>
<p>以下配置可用于 <code>configure()</code> 或单个 Toast 的第二个参数。</p>
<table>
<tr><th>配置项</th><th>类型</th><th>默认值</th><th>说明</th></tr>
<tr><td>position</td><td>string</td><td>'top-right'</td><td>位置:top-left/center/right、bottom-left/center/right</td></tr>
<tr><td>duration</td><td>number</td><td>4000</td><td>显示时长(ms)0=不自动关闭</td></tr>
<tr><td>max</td><td>number</td><td>6</td><td>同时最多显示条数</td></tr>
<tr><td>gap</td><td>number</td><td>12</td><td>Toast 之间的间距(px)</td></tr>
<tr><td>offset</td><td>number</td><td>24</td><td>容器边距(px)</td></tr>
<tr><td>pauseOnHover</td><td>boolean</td><td>true</td><td>悬停时暂停计时</td></tr>
<tr><td>closeOnClick</td><td>boolean</td><td>true</td><td>点击 Toast 关闭</td></tr>
<tr><td>draggable</td><td>boolean</td><td>true</td><td>允许拖拽关闭</td></tr>
<tr><td>showProgress</td><td>boolean</td><td>true</td><td>显示进度条</td></tr>
<tr><td>progressDirection</td><td>string</td><td>'horizontal'</td><td>进度条方向:horizontal / vertical</td></tr>
<tr><td>icon</td><td>boolean</td><td>true</td><td>显示类型图标</td></tr>
<tr><td>closeButton</td><td>boolean</td><td>true</td><td>显示关闭按钮</td></tr>
<tr><td>theme</td><td>string</td><td>'auto'</td><td>主题:light / dark / auto / warm</td></tr>
<tr><td>animation</td><td>string</td><td>'slide'</td><td>动画效果(见下方列表)</td></tr>
<tr><td>zIndex</td><td>number</td><td>9999</td><td>CSS z-index</td></tr>
<tr><td>width</td><td>number/string</td><td>360</td><td>Toast 宽度(px)</td></tr>
<tr><td>className</td><td>string</td><td>''</td><td>附加 CSS 类名</td></tr>
<tr><td>style</td><td>object</td><td>{}</td><td>内联样式对象</td></tr>
<tr><td>locale</td><td>string</td><td>'zh-CN'</td><td>语言代码</td></tr>
<tr><td>resetTimerOnUpdate</td><td>boolean</td><td>false</td><td>update()时重置计时器 <span class="badge badge-new">v2.0</span></td></tr>
<tr><td>notifyWhenHidden</td><td>boolean</td><td>false</td><td>页面不可见时发系统通知 <span class="badge badge-new">v2.0</span></td></tr>
<tr><td>render</td><td>function</td><td></td><td>自定义渲染函数 <span class="badge badge-new">v2.0</span></td></tr>
</table>
<h2 id="callbacks">回调函数</h2>
<table>
<tr><th>回调</th><th>签名</th><th>触发时机</th></tr>
<tr><td>onShow</td><td>(toast) => void</td><td>Toast 显示后</td></tr>
<tr><td>onClose</td><td>(toast) => void</td><td>Toast 关闭后</td></tr>
<tr><td>onClick</td><td>(toast) => void</td><td>Toast 被点击时</td></tr>
<tr><td>onUpdate</td><td>(toast) => void</td><td>Toast 内容更新后 <span class="badge badge-new">v2.0</span></td></tr>
</table>
<h2 id="animations">动画列表</h2>
<table>
<tr><th>名称</th><th>效果</th><th>时长</th></tr>
<tr><td>slide</td><td>从右侧滑入 + 回弹</td><td>400ms</td></tr>
<tr><td>fade</td><td>纯淡入 (blur→清晰)</td><td>500ms</td></tr>
<tr><td>scale</td><td>弹性放大 (0.55→1.07→1)</td><td>450ms</td></tr>
<tr><td>bounce</td><td>从天而降四段弹跳</td><td>650ms</td></tr>
<tr><td>flip</td><td>3D 翻转入场 + 回摆</td><td>500ms</td></tr>
<tr><td>rotate</td><td>旋转摇摆进入</td><td>500ms</td></tr>
<tr><td>zoom</td><td>从中心爆发式弹出</td><td>500ms</td></tr>
<tr><td>slideUp</td><td>从下方弹入</td><td>400ms</td></tr>
<tr><td>slideDown</td><td>从上方弹入</td><td>400ms</td></tr>
<tr><td>slideLeft</td><td>从左侧滑入</td><td>400ms</td></tr>
<tr><td>slideRight</td><td>从右侧滑入</td><td>400ms</td></tr>
</table>
<h2 id="themes">主题参考</h2>
<pre><span class="cm">// 内置主题</span>
light <span class="cm">— 白色玻璃质感</span>
dark <span class="cm">— 深色玻璃质感</span>
auto <span class="cm">— 跟随系统</span>
warm <span class="cm">— 暖色调</span>
<span class="cm">// 注册自定义主题</span>
MeToast.themes.<span class="fn">registerTheme</span>(<span class="s">'ocean'</span>, {
<span class="fn">bg</span>: <span class="s">'rgba(240,249,255,0.96)'</span>,
<span class="fn">text</span>: <span class="s">'#0c4a6e'</span>,
<span class="fn">border</span>: <span class="s">'rgba(14,165,233,0.2)'</span>,
<span class="fn">shadow</span>: <span class="s">'0 10px 36px -10px rgba(14,165,233,0.18)'</span>,
<span class="fn">hoverShadow</span>: <span class="s">'0 14px 48px -10px rgba(14,165,233,0.22)'</span>,
<span class="fn">progressBg</span>: <span class="s">'rgba(14,165,233,0.1)'</span>,
<span class="fn">closeHoverBg</span>: <span class="s">'rgba(14,165,233,0.1)'</span>,
});
MeToast.themes.<span class="fn">switchTheme</span>(<span class="s">'ocean'</span>);</pre>
</main>
<script src="../dist/metona-toast.js"></script>
<script>
MeToast.configure({ position: 'top-right', duration: 4000, theme: 'dark', animation: 'slide' });
// 侧边栏高亮
document.querySelectorAll('.sidebar a[href^="#"]').forEach(a => {
a.addEventListener('click', () => {
document.querySelectorAll('.sidebar a').forEach(x => x.classList.remove('active'));
a.classList.add('active');
});
});
</script>
</body>
</html>
+222
View File
@@ -0,0 +1,222 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MetonaToast — 轻量精致的 Toast 通知库</title>
<meta name="description" content="MetonaToast 是轻量、零依赖、精致美观的 Toast 通知库。80+ 图标类型、11 种动画、主题系统、国际化、插件架构。">
<style>
:root {
--bg: #0b1121; --bg2: #111827; --surface: #1a2332;
--border: #1e2d4d; --text: #e2e8f0; --muted: #64748b;
--accent: #6366f1; --accent2: #818cf8; --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.7; }
a { color: var(--accent2); text-decoration: none; }
code { background: rgba(99,102,241,0.12); color: #a5b4fc; padding: 2px 7px; border-radius: 5px; font-size: 0.9em; }
pre { background: #0a0f1a; border: 1px solid var(--border); border-radius: 10px; padding: 18px 22px; overflow-x: auto; font: 13px/1.8 'SF Mono','Cascadia Code','Fira Code',Consolas,monospace; color: #cbd5e1; }
pre .kw { color: #c084fc; } pre .fn { color: #60a5fa; } pre .s { color: #34d399; } pre .cm { color: #475569; }
.nav { position: sticky; top: 0; z-index: 100; background: rgba(11,17,33,0.88); backdrop-filter: blur(14px); border-bottom: 1px solid var(--border); }
.nav-inner { max-width: 1200px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; height: 56px; }
.nav-logo { font-size: 18px; font-weight: 700; background: linear-gradient(135deg, #818cf8, #c084fc, #f472b6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.nav-links { display: flex; gap: 24px; }
.nav-links a { color: var(--muted); font-size: 14px; font-weight: 500; transition: .2s; }
.nav-links a:hover { color: var(--accent2); }
.hero { text-align: center; padding: 100px 20px 70px; background: radial-gradient(ellipse at 50% 0%, rgba(99,102,241,0.18) 0%, transparent 60%), linear-gradient(180deg, #0f172a 0%, var(--bg) 100%); border-bottom: 1px solid var(--border); }
.hero h1 { font-size: 52px; font-weight: 800; }
.hero h1 span { background: linear-gradient(135deg, #818cf8, #c084fc, #f472b6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.hero p { color: var(--muted); font-size: 17px; margin-top: 12px; max-width: 600px; margin-left: auto; margin-right: auto; }
.hero .badges { display: flex; gap: 12px; justify-content: center; margin-top: 24px; flex-wrap: wrap; }
.hero .badges span { background: var(--surface); border: 1px solid var(--border); border-radius: 20px; padding: 6px 18px; font-size: 13px; color: var(--accent2); }
.hero-actions { margin-top: 32px; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.btn { padding: 11px 28px; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; transition: .2s; border: none; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent2); transform: translateY(-1px); }
.btn-outline { background: transparent; border: 1px solid var(--border); color: var(--text); }
.btn-outline:hover { border-color: var(--accent); color: var(--accent2); }
.container { max-width: 1100px; margin: 0 auto; padding: 60px 24px; }
.section-title { font-size: 28px; font-weight: 700; text-align: center; margin-bottom: 8px; }
.section-desc { color: var(--muted); text-align: center; margin-bottom: 40px; font-size: 15px; }
.features { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; }
.feature { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 28px; transition: .2s; }
.feature:hover { border-color: var(--accent); }
.feature-icon { font-size: 28px; margin-bottom: 12px; }
.feature h3 { font-size: 16px; margin-bottom: 6px; }
.feature p { color: var(--muted); font-size: 13px; line-height: 1.6; }
.code-block { max-width: 750px; margin: 0 auto; }
.theme-demo { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin: 24px 0; }
.theme-btn { padding: 10px 20px; border-radius: 8px; border: 1px solid var(--border); cursor: pointer; background: var(--surface); color: var(--text); font-size: 13px; font-weight: 500; transition: .2s; }
.theme-btn:hover { border-color: var(--accent); }
footer { text-align: center; padding: 40px 24px; color: var(--muted); font-size: 13px; border-top: 1px solid var(--border); }
@media (max-width: 768px) {
.hero h1 { font-size: 32px; }
.nav-links { gap: 14px; }
}
</style>
</head>
<body>
<nav class="nav">
<div class="nav-inner">
<div class="nav-logo">MetonaToast</div>
<div class="nav-links">
<a href="index.html">首页</a>
<a href="docs.html">文档</a>
<a href="demo.html">演示</a>
<a href="https://github.com/metona/metona-toast" target="_blank">GitHub</a>
</div>
</div>
</nav>
<header class="hero">
<h1><span>MetonaToast</span></h1>
<p>轻量 · 零依赖 · 精致美观的 Toast 通知库。纯原生 JavaScriptgzip 不到 10KB。</p>
<div class="badges">
<span>📦 &lt;10KB gzip</span><span>🎨 107 图标</span><span>🎭 11 动画</span><span>🌍 国际化</span><span>🔌 插件</span><span>📘 TypeScript</span>
</div>
<div class="hero-actions">
<button class="btn btn-primary" onclick="demoQuickStart()">快速预览</button>
<button class="btn btn-outline" onclick="location.href='docs.html'">查看文档</button>
</div>
</header>
<main class="container">
<h2 class="section-title">核心特性</h2>
<p class="section-desc">一切你需要的,都在一个不到 10KB 的包中</p>
<div class="features">
<div class="feature">
<div class="feature-icon">🎨</div>
<h3>107 种图标类型</h3>
<p>success / error / warning / info / loading 以及 100+ 扩展类型,覆盖所有常见场景。</p>
</div>
<div class="feature">
<div class="feature-icon">🎭</div>
<h3>11 种动画效果</h3>
<p>slide / fade / scale / bounce / flip / rotate / zoom / slideUp / slideDown / slideLeft / slideRight,每种感官差异明显。</p>
</div>
<div class="feature">
<div class="feature-icon">🖌️</div>
<h3>主题系统</h3>
<p>内置 light / dark / auto / warm 四种主题,支持 <code>registerTheme()</code> 自定义注册任意主题。</p>
</div>
<div class="feature">
<div class="feature-icon">🌍</div>
<h3>国际化</h3>
<p>内置 zh-CN 和 en-US 完整翻译,400+ 词汇。支持 <code>addTranslations()</code> 扩展任意语言。</p>
</div>
<div class="feature">
<div class="feature-icon">🔌</div>
<h3>插件系统</h3>
<p>3 款预设插件 + 自定义插件架构。keyboard / persistence / accessibility 开箱即用。</p>
</div>
<div class="feature">
<div class="feature-icon"></div>
<h3>拖拽关闭</h3>
<p>任意方向拖动 Toast 即可关闭。支持 Pointer Events,移动端和桌面端体验一致。</p>
</div>
<div class="feature">
<div class="feature-icon"></div>
<h3>零依赖</h3>
<p>纯原生 JavaScript 实现,无需任何运行时依赖。兼容所有现代浏览器。</p>
</div>
<div class="feature">
<div class="feature-icon">📘</div>
<h3>TypeScript</h3>
<p>完整类型定义,智能提示全覆盖。<code>MeToast.success()</code><code>.confirm()</code><code>.action()</code> 全部有类型。</p>
</div>
</div>
</main>
<section class="container">
<h2 class="section-title">快速开始</h2>
<p class="section-desc">一行安装,三行代码即可使用</p>
<div class="code-block">
<pre><span class="cm"># npm 安装</span>
npm install metona-toast
<span class="cm">// 导入</span>
<span class="kw">import</span> MeToast <span class="kw">from</span> <span class="s">'metona-toast'</span>;
<span class="cm">// 使用</span>
MeToast.<span class="fn">success</span>(<span class="s">'操作成功!'</span>);
MeToast.<span class="fn">error</span>(<span class="s">'操作失败!'</span>);
MeToast.<span class="fn">loading</span>(<span class="s">'加载中...'</span>);
<span class="cm">// CDN 直接引用</span>
<span class="kw">&lt;script</span> <span class="fn">src</span>=<span class="s">"https://unpkg.com/metona-toast/dist/metona-toast.min.js"</span><span class="kw">&gt;&lt;/script&gt;</span></pre>
</div>
</section>
<section class="container">
<h2 class="section-title">高级功能速览</h2>
<p class="section-desc">不仅仅是一个 Toast——确认框、输入框、进度条、倒计时、队列,应有尽有</p>
<div class="code-block">
<pre><span class="cm">// Promise 风格 — 自动 loading → success/error</span>
<span class="kw">await</span> MeToast.<span class="fn">promise</span>(<span class="fn">fetch</span>(<span class="s">'/api/data'</span>), {
<span class="fn">loading</span>: <span class="s">'加载中...'</span>,
<span class="fn">success</span>: <span class="s">'加载完成!'</span>,
<span class="fn">error</span>: <span class="s">'加载失败'</span>,
});
<span class="cm">// 确认对话框 → Promise&lt;boolean&gt;</span>
<span class="kw">const</span> ok = <span class="kw">await</span> MeToast.<span class="fn">confirm</span>(<span class="s">'确定删除吗?'</span>, { <span class="fn">confirmText</span>: <span class="s">'删除'</span> });
<span class="cm">// 输入对话框 → Promise&lt;string|null&gt;</span>
<span class="kw">const</span> name = <span class="kw">await</span> MeToast.<span class="fn">prompt</span>(<span class="s">'请输入姓名'</span>);
<span class="cm">// Action Toast — 内嵌操作按钮</span>
MeToast.<span class="fn">action</span>(<span class="s">'文件已删除'</span>, [
{ <span class="fn">text</span>: <span class="s">'撤销'</span>, <span class="fn">onClick</span>: () => <span class="fn">restore</span>() },
{ <span class="fn">text</span>: <span class="s">'查看'</span>, <span class="fn">onClick</span>: () => <span class="fn">open</span>() },
]);
<span class="cm">// Toast 分组管理</span>
<span class="kw">const</span> orderGroup = MeToast.<span class="fn">group</span>(<span class="s">'orders'</span>);
orderGroup.<span class="fn">success</span>(<span class="s">'订单已创建'</span>);
orderGroup.<span class="fn">dismiss</span>(); <span class="cm">// 一键关闭整组</span></pre>
</div>
</section>
<section class="container">
<h2 class="section-title">主题切换</h2>
<p class="section-desc">点击按钮实时预览四种内置主题</p>
<div class="theme-demo">
<button class="theme-btn" onclick="switchTheme('light')">☀️ 浅色 Light</button>
<button class="theme-btn" onclick="switchTheme('dark')">🌙 深色 Dark</button>
<button class="theme-btn" onclick="switchTheme('auto')">🔄 自动 Auto</button>
<button class="theme-btn" onclick="switchTheme('warm')">🔥 暖色 Warm</button>
</div>
</section>
<footer>
<p>MetonaToast v2.0.0 · MIT License · <a href="https://github.com/metona/metona-toast">GitHub</a></p>
</footer>
<script src="../dist/metona-toast.js"></script>
<script>
MeToast.configure({ position: 'top-right', duration: 4000, theme: 'dark', animation: 'slide' });
function demoQuickStart() {
MeToast.success('✨ 欢迎使用 MetonaToast');
setTimeout(() => MeToast.info('💡 试试点击右上角的 Toast 来关闭它'), 800);
setTimeout(() => MeToast.warning('⚠️ 你也可以拖动 Toast 来关闭'), 1600);
}
function switchTheme(t) {
MeToast.themes.switchTheme(t);
MeToast.configure({ theme: t });
const names = { light: '浅色', dark: '深色', auto: '自动', warm: '暖色' };
MeToast.info('已切换到「' + names[t] + '」主题', { theme: t });
}
</script>
</body>
</html>