init: MetonaToast v2.0.0 基线 — 已完成改进(模板抽象/constants拆分/测试增强)
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
coverage/
|
||||
.DS_Store
|
||||
@@ -0,0 +1,232 @@
|
||||
# MetonaToast
|
||||
|
||||
轻量、零依赖、精致美观的 Toast 通知库。
|
||||
|
||||
[](https://www.npmjs.com/package/metona-toast)
|
||||
[](https://bundlephobia.com/package/metona-toast)
|
||||
[](https://github.com/metona/metona-toast/blob/main/LICENSE)
|
||||
|
||||
## 特性
|
||||
|
||||
- **零依赖** — 纯原生 JavaScript,gzip 后不到 10KB
|
||||
- **80+ 图标类型** — success/error/warning/info/loading 及更多扩展类型
|
||||
- **11 种动画** — slide/fade/scale/bounce/flip/rotate/zoom/slideUp/slideDown/slideLeft/slideRight,每种效果明显不同
|
||||
- **主题系统** — light/dark/auto/warm 四种内置主题,支持 `registerTheme()` 注册自定义主题
|
||||
- **国际化** — 内置 zh-CN / en-US 完整翻译,可通过 `addTranslations()` 扩展
|
||||
- **插件系统** — 3 款预设插件 (keyboard/persistence/accessibility) + 自定义插件
|
||||
- **可拖拽关闭** — 拖动 Toast 任意方向即可关闭
|
||||
- **TypeScript** — 完整类型定义
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
npm install metona-toast
|
||||
```
|
||||
|
||||
### 引用方式
|
||||
|
||||
```javascript
|
||||
// ES Module
|
||||
import MeToast from 'metona-toast';
|
||||
|
||||
// CommonJS
|
||||
const MeToast = require('metona-toast');
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- CDN UMD 开发版 -->
|
||||
<script src="https://unpkg.com/metona-toast@2.0.0/dist/metona-toast.js"></script>
|
||||
|
||||
<!-- CDN 压缩版(生产环境推荐) -->
|
||||
<script src="https://unpkg.com/metona-toast@2.0.0/dist/metona-toast.min.js"></script>
|
||||
|
||||
<!-- CDN ES Module -->
|
||||
<script type="module">
|
||||
import MeToast from 'https://unpkg.com/metona-toast@2.0.0/dist/metona-toast.esm.js';
|
||||
</script>
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```javascript
|
||||
import MeToast from 'metona-toast';
|
||||
|
||||
// 基础用法
|
||||
MeToast.success('操作成功!');
|
||||
MeToast.error('操作失败!');
|
||||
MeToast.warning('请注意检查');
|
||||
MeToast.info('系统维护中');
|
||||
|
||||
// 带标题
|
||||
MeToast.success({ title: '保存成功', message: '文件已同步到云端' });
|
||||
|
||||
// 加载 → 成功链式转换
|
||||
const loading = MeToast.loading('正在提交...');
|
||||
setTimeout(() => loading.success('提交成功!'), 2000);
|
||||
|
||||
// Promise 风格
|
||||
await MeToast.promise(fetch('/api/data'), {
|
||||
loading: '加载中...',
|
||||
success: '加载完成!',
|
||||
error: '加载失败',
|
||||
});
|
||||
```
|
||||
|
||||
## 全局配置
|
||||
|
||||
```javascript
|
||||
MeToast.configure({
|
||||
position: 'top-right', // top-left | top-center | top-right | bottom-left | bottom-center | bottom-right
|
||||
duration: 4000, // 显示时长(ms),0 为不自动关闭
|
||||
max: 6, // 同时最多显示条数
|
||||
theme: 'auto', // light | dark | auto | warm
|
||||
animation: 'slide', // slide | fade | scale | bounce | flip | rotate | zoom | slideUp | slideDown | slideLeft | slideRight
|
||||
pauseOnHover: true, // 悬停暂停计时
|
||||
closeOnClick: true, // 点击关闭
|
||||
showProgress: true, // 显示进度条
|
||||
draggable: true, // 允许拖拽关闭
|
||||
locale: 'zh-CN', // 语言
|
||||
});
|
||||
```
|
||||
|
||||
## 动画效果
|
||||
|
||||
11 种动画,每种感官差异明显:
|
||||
|
||||
| 动画 | 效果描述 |
|
||||
|------|----------|
|
||||
| `slide` | 从右侧滑入 + 轻微过冲回弹 |
|
||||
| `fade` | 纯淡入(blur→清晰),从容优雅 |
|
||||
| `scale` | 弹性放大(0.55→1.07→1.0) |
|
||||
| `bounce` | 从天而降四段弹跳 |
|
||||
| `flip` | 3D 翻转入场 + 回摆 |
|
||||
| `rotate` | 旋转摇摆进入 |
|
||||
| `zoom` | 从中心爆发式弹出 |
|
||||
| `slideUp` | 从下方弹入 |
|
||||
| `slideDown` | 从上方弹入 |
|
||||
| `slideLeft` | 从左侧滑入 |
|
||||
| `slideRight` | 从右侧滑入 |
|
||||
|
||||
```javascript
|
||||
MeToast.success('弹跳动画', { animation: 'bounce' });
|
||||
|
||||
// 注册自定义动画
|
||||
MeToast.animations.register('myAnim', {
|
||||
enter: { transform: 'rotate(-30deg) scale(0.5)', opacity: 0 },
|
||||
leave: { transform: 'rotate(0) scale(1)', opacity: 1 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||
});
|
||||
```
|
||||
|
||||
## 主题
|
||||
|
||||
```javascript
|
||||
// 切换主题
|
||||
MeToast.themes.switchTheme('dark');
|
||||
MeToast.themes.switchTheme('warm');
|
||||
|
||||
// 注册自定义主题
|
||||
MeToast.themes.registerTheme('ocean', {
|
||||
bg: 'rgba(240, 249, 255, 0.96)',
|
||||
text: '#0c4a6e',
|
||||
border: 'rgba(14, 165, 233, 0.2)',
|
||||
shadow: '0 10px 36px -10px rgba(14, 165, 233, 0.18)',
|
||||
hoverShadow: '0 14px 48px -10px rgba(14, 165, 233, 0.22)',
|
||||
progressBg: 'rgba(14, 165, 233, 0.1)',
|
||||
closeHoverBg: 'rgba(14, 165, 233, 0.1)',
|
||||
});
|
||||
MeToast.themes.switchTheme('ocean');
|
||||
```
|
||||
|
||||
## 高级功能
|
||||
|
||||
```javascript
|
||||
// 确认对话框 → Promise<boolean>
|
||||
const ok = await MeToast.confirm('确定删除?', { confirmText: '删除', confirmColor: '#ef4444' });
|
||||
|
||||
// 输入对话框 → Promise<string|null>
|
||||
const name = await MeToast.prompt('请输入姓名', { placeholder: '请输入...' });
|
||||
|
||||
// 进度条
|
||||
const p = MeToast.progress('上传中...');
|
||||
p.setProgress(60);
|
||||
p.complete('上传完成!');
|
||||
|
||||
// 倒计时({seconds} 自动替换)
|
||||
MeToast.countdown('操作将在 {seconds} 秒后执行', 5, {
|
||||
onComplete: () => MeToast.success('已执行'),
|
||||
});
|
||||
|
||||
// 队列展示(顺序逐个)
|
||||
await MeToast.queue(['步骤一', '步骤二', '步骤三'], { delay: 800 });
|
||||
|
||||
// 堆叠展示(同时错峰)
|
||||
MeToast.stack(['消息1', '消息2', '消息3'], { stagger: 150 });
|
||||
```
|
||||
|
||||
## 插件
|
||||
|
||||
```javascript
|
||||
// 安装预设插件
|
||||
MeToast.use('keyboard'); // ESC 关闭所有 Toast
|
||||
MeToast.use('persistence'); // 配置持久化到 localStorage
|
||||
MeToast.use('accessibility'); // 屏幕阅读器朗读公告
|
||||
|
||||
// 自定义插件(通过 install 中注册 Toast 生命周期钩子)
|
||||
import MeToast, { Toast } from 'metona-toast';
|
||||
|
||||
MeToast.use({
|
||||
name: 'my-plugin',
|
||||
install() {
|
||||
Toast.on('afterShow', (toast) => {
|
||||
console.log('Toast shown:', toast.message);
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 国际化
|
||||
|
||||
```javascript
|
||||
MeToast.i18n.switchLocale('en-US');
|
||||
|
||||
// 添加语言
|
||||
MeToast.i18n.addTranslations('ja', {
|
||||
success: '成功',
|
||||
error: 'エラー',
|
||||
confirm: '確認',
|
||||
cancel: 'キャンセル',
|
||||
});
|
||||
|
||||
// 格式化工具
|
||||
MeToast.i18n.formatNumber(1234567); // "1,234,567"
|
||||
MeToast.i18n.formatCurrency(99, 'CNY'); // "¥99.00"
|
||||
```
|
||||
|
||||
## Toast 管理
|
||||
|
||||
```javascript
|
||||
MeToast.count(); // 当前数量
|
||||
MeToast.getToasts(); // 获取所有实例
|
||||
MeToast.dismiss(); // 关闭所有
|
||||
MeToast.dismiss(id); // 关闭指定
|
||||
MeToast.pauseAll(); // 暂停所有计时
|
||||
MeToast.resumeAll(); // 恢复所有计时
|
||||
MeToast.destroy(); // 完全销毁
|
||||
```
|
||||
|
||||
## 回调
|
||||
|
||||
```javascript
|
||||
MeToast.success({
|
||||
message: '操作成功',
|
||||
onShow: (toast) => console.log('显示:', toast.id),
|
||||
onClose: (toast) => console.log('关闭:', toast.id),
|
||||
onClick: (toast) => console.log('点击:', toast.id),
|
||||
});
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current',
|
||||
},
|
||||
modules: 'commonjs',
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-modules-commonjs',
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MetonaToast 构建脚本
|
||||
|
||||
echo "🍞 MetonaToast 构建脚本"
|
||||
echo "========================"
|
||||
|
||||
# 检查Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo "❌ 错误: 未找到Node.js"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查npm
|
||||
if ! command -v npm &> /dev/null; then
|
||||
echo "❌ 错误: 未找到npm"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Node.js版本: $(node -v)"
|
||||
echo "✅ npm版本: $(npm -v)"
|
||||
|
||||
# 安装依赖
|
||||
echo ""
|
||||
echo "📦 安装依赖..."
|
||||
npm install
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ 依赖安装失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 依赖安装完成"
|
||||
|
||||
# 运行测试
|
||||
echo ""
|
||||
echo "🧪 运行测试..."
|
||||
npm test
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ 测试失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 测试通过"
|
||||
|
||||
# 构建项目
|
||||
echo ""
|
||||
echo "🔨 构建项目..."
|
||||
npm run build
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ 构建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 构建完成"
|
||||
|
||||
# 显示构建结果
|
||||
echo ""
|
||||
echo "📊 构建结果:"
|
||||
echo "========================"
|
||||
ls -lh dist/
|
||||
|
||||
echo ""
|
||||
echo "✅ 构建成功完成!"
|
||||
echo ""
|
||||
echo "📁 文件结构:"
|
||||
echo " - dist/metona-toast.js (UMD格式)"
|
||||
echo " - dist/metona-toast.esm.js (ES Module格式)"
|
||||
echo " - dist/metona-toast.cjs.js (CommonJS格式)"
|
||||
echo " - dist/metona-toast.min.js (压缩版本)"
|
||||
echo " - dist/metona-toast.d.ts (TypeScript声明)"
|
||||
echo ""
|
||||
echo "🚀 使用方法:"
|
||||
echo " 1. 浏览器: <script src='dist/metona-toast.js'></script>"
|
||||
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 ""
|
||||
@@ -0,0 +1,395 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,442 @@
|
||||
<!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>📦 <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"><!-- UMD 开发版 --></span>
|
||||
<span class="kw"><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">></script></span>
|
||||
|
||||
<span class="cm"><!-- 压缩版(生产环境推荐)--></span>
|
||||
<span class="kw"><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">></script></span>
|
||||
|
||||
<span class="cm"><!-- ES Module 直接引用 --></span>
|
||||
<span class="kw"><script</span> <span class="fn">type</span>=<span class="s">"module"</span><span class="kw">></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"></script></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<boolean></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<string|null></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>
|
||||
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
testEnvironment: 'jsdom',
|
||||
transform: {
|
||||
'^.+\\.js$': 'babel-jest',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'/node_modules/(?!(@rollup)/)',
|
||||
],
|
||||
moduleFileExtensions: ['js', 'json'],
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.js',
|
||||
'!src/index.js',
|
||||
],
|
||||
coverageDirectory: 'coverage',
|
||||
coverageReporters: ['text', 'lcov'],
|
||||
verbose: true,
|
||||
};
|
||||
Generated
+7444
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "metona-toast",
|
||||
"version": "2.0.0",
|
||||
"description": "轻量、零依赖、精致美观的Toast通知库。单文件,开箱即用。",
|
||||
"main": "dist/metona-toast.js",
|
||||
"module": "src/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.js",
|
||||
"require": "./dist/metona-toast.js",
|
||||
"types": "./types/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
"types/",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"lint": "eslint src/",
|
||||
"lint:fix": "eslint src/ --fix",
|
||||
"format": "prettier --write src/",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"prepublishOnly": "npm run build",
|
||||
"docs": "jsdoc src/ -d docs",
|
||||
"example": "serve examples/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/metona/metona-toast.git"
|
||||
},
|
||||
"keywords": [
|
||||
"toast",
|
||||
"notification",
|
||||
"alert",
|
||||
"message",
|
||||
"snackbar",
|
||||
"ui",
|
||||
"component",
|
||||
"lightweight",
|
||||
"zero-dependency"
|
||||
],
|
||||
"author": "metona",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/metona/metona-toast/issues"
|
||||
},
|
||||
"homepage": "https://github.com/metona/metona-toast#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.0",
|
||||
"@babel/preset-env": "^7.22.0",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.22.0",
|
||||
"@rollup/plugin-commonjs": "^25.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-terser": "^0.4.0",
|
||||
"@types/jest": "^29.5.0",
|
||||
"babel-jest": "^29.5.0",
|
||||
"eslint": "^8.40.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"prettier": "^2.8.0",
|
||||
"rollup": "^3.20.0",
|
||||
"rollup-plugin-dts": "^5.3.0",
|
||||
"rollup-plugin-livereload": "^2.0.5",
|
||||
"rollup-plugin-serve": "^2.0.1",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import terser from '@rollup/plugin-terser';
|
||||
import dts from 'rollup-plugin-dts';
|
||||
import serve from 'rollup-plugin-serve';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
|
||||
const isDev = process.env.ROLLUP_WATCH;
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
|
||||
// 基础配置
|
||||
const baseConfig = {
|
||||
input: 'src/index.js',
|
||||
plugins: [
|
||||
resolve(),
|
||||
commonjs(),
|
||||
],
|
||||
};
|
||||
|
||||
// 开发服务器配置
|
||||
const devPlugins = isDev ? [
|
||||
serve({
|
||||
open: true,
|
||||
contentBase: ['examples', 'dist'],
|
||||
port: 3000,
|
||||
}),
|
||||
livereload({
|
||||
watch: ['src', 'examples'],
|
||||
}),
|
||||
] : [];
|
||||
|
||||
// 生产环境配置
|
||||
const prodPlugins = isProd ? [
|
||||
terser({
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
pure_funcs: ['console.log', 'console.warn'],
|
||||
},
|
||||
format: {
|
||||
comments: false,
|
||||
},
|
||||
}),
|
||||
] : [];
|
||||
|
||||
// 输出配置
|
||||
export default [
|
||||
// UMD格式(浏览器)
|
||||
{
|
||||
...baseConfig,
|
||||
output: {
|
||||
file: 'dist/metona-toast.js',
|
||||
format: 'umd',
|
||||
name: 'MeToast',
|
||||
exports: 'named',
|
||||
sourcemap: !isProd,
|
||||
globals: {},
|
||||
},
|
||||
plugins: [
|
||||
...baseConfig.plugins,
|
||||
...devPlugins,
|
||||
...prodPlugins,
|
||||
],
|
||||
},
|
||||
|
||||
// ESM格式(现代浏览器/打包工具)
|
||||
{
|
||||
...baseConfig,
|
||||
output: {
|
||||
file: 'dist/metona-toast.esm.js',
|
||||
format: 'es',
|
||||
exports: 'named',
|
||||
sourcemap: !isProd,
|
||||
},
|
||||
plugins: [
|
||||
...baseConfig.plugins,
|
||||
...prodPlugins,
|
||||
],
|
||||
},
|
||||
|
||||
// CommonJS格式(Node.js)
|
||||
{
|
||||
...baseConfig,
|
||||
output: {
|
||||
file: 'dist/metona-toast.cjs.js',
|
||||
format: 'cjs',
|
||||
exports: 'named',
|
||||
sourcemap: !isProd,
|
||||
},
|
||||
plugins: [
|
||||
...baseConfig.plugins,
|
||||
...prodPlugins,
|
||||
],
|
||||
},
|
||||
|
||||
// 压缩版本(UMD)
|
||||
{
|
||||
...baseConfig,
|
||||
output: {
|
||||
file: 'dist/metona-toast.min.js',
|
||||
format: 'umd',
|
||||
name: 'MeToast',
|
||||
exports: 'named',
|
||||
sourcemap: false,
|
||||
globals: {},
|
||||
},
|
||||
plugins: [
|
||||
...baseConfig.plugins,
|
||||
terser({
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
pure_funcs: ['console.log', 'console.warn'],
|
||||
passes: 2,
|
||||
},
|
||||
format: {
|
||||
comments: false,
|
||||
},
|
||||
mangle: {
|
||||
toplevel: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
// TypeScript声明文件
|
||||
{
|
||||
input: 'types/index.d.ts',
|
||||
output: {
|
||||
file: 'dist/metona-toast.d.ts',
|
||||
format: 'es',
|
||||
},
|
||||
plugins: [dts()],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MetonaToast 本地开发服务器
|
||||
|
||||
echo "🍞 MetonaToast 开发服务器"
|
||||
echo "========================="
|
||||
|
||||
# 检查Python
|
||||
if command -v python3 &> /dev/null; then
|
||||
PYTHON=python3
|
||||
elif command -v python &> /dev/null; then
|
||||
PYTHON=python
|
||||
else
|
||||
echo "❌ 错误: 未找到Python"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PORT=${1:-3000}
|
||||
|
||||
echo "✅ Python: $($PYTHON --version 2>&1)"
|
||||
echo "🌐 启动服务器..."
|
||||
echo ""
|
||||
echo "📁 访问地址:"
|
||||
echo " http://localhost:$PORT/examples/demo.html"
|
||||
echo " http://localhost:$PORT/examples/index.html"
|
||||
echo ""
|
||||
echo "按 Ctrl+C 停止服务器"
|
||||
echo ""
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
$PYTHON -m http.server $PORT
|
||||
@@ -0,0 +1,860 @@
|
||||
/**
|
||||
* MetonaToast Animations - 动画管理
|
||||
* @module animations
|
||||
* @version 2.0.0
|
||||
* @description 动画效果管理和自定义
|
||||
*/
|
||||
|
||||
import { ANIMATIONS } from './constants.js';
|
||||
|
||||
// 动画缓存
|
||||
const animationCache = new Map();
|
||||
|
||||
/**
|
||||
* 动画管理器类
|
||||
*/
|
||||
class AnimationManager {
|
||||
constructor() {
|
||||
this.animations = new Map();
|
||||
this.activeAnimations = new Map();
|
||||
this.animationId = 0;
|
||||
|
||||
// 注册默认动画
|
||||
this._registerDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册默认动画
|
||||
*/
|
||||
_registerDefaults() {
|
||||
Object.entries(ANIMATIONS).forEach(([name, config]) => {
|
||||
this.register(name, config);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册动画
|
||||
* @param {string} name - 动画名称
|
||||
* @param {Object} config - 动画配置
|
||||
*/
|
||||
register(name, config) {
|
||||
const animation = {
|
||||
name,
|
||||
enter: config.enter || {},
|
||||
leave: config.leave || {},
|
||||
duration: config.duration || 300,
|
||||
easing: config.easing || 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
delay: config.delay || 0,
|
||||
iterations: config.iterations || 1,
|
||||
direction: config.direction || 'normal',
|
||||
fillMode: config.fillMode || 'forwards',
|
||||
};
|
||||
|
||||
this.animations.set(name, animation);
|
||||
animationCache.set(name, animation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销动画
|
||||
* @param {string} name - 动画名称
|
||||
*/
|
||||
unregister(name) {
|
||||
this.animations.delete(name);
|
||||
animationCache.delete(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @param {string} name - 动画名称
|
||||
* @returns {Object|null} 动画配置
|
||||
*/
|
||||
get(name) {
|
||||
return this.animations.get(name) || ANIMATIONS[name] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
apply(element, animationName, options = {}) {
|
||||
return new Promise((resolve) => {
|
||||
const animation = this.get(animationName);
|
||||
if (!animation) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const config = { ...animation, ...options };
|
||||
const animId = ++this.animationId;
|
||||
|
||||
// 设置初始状态
|
||||
Object.assign(element.style, config.enter);
|
||||
|
||||
// 强制重绘
|
||||
element.offsetHeight;
|
||||
|
||||
// 创建动画
|
||||
const keyframes = [
|
||||
{ ...config.enter },
|
||||
{ ...config.leave },
|
||||
];
|
||||
|
||||
const animationOptions = {
|
||||
duration: config.duration,
|
||||
easing: config.easing,
|
||||
delay: config.delay,
|
||||
iterations: config.iterations,
|
||||
direction: config.direction,
|
||||
fill: config.fillMode,
|
||||
};
|
||||
|
||||
// 应用动画
|
||||
const anim = element.animate(keyframes, animationOptions);
|
||||
|
||||
// 存储活动动画
|
||||
this.activeAnimations.set(animId, {
|
||||
element,
|
||||
animation: anim,
|
||||
config,
|
||||
});
|
||||
|
||||
// 动画完成处理
|
||||
anim.onfinish = () => {
|
||||
// 应用最终状态
|
||||
Object.assign(element.style, config.leave);
|
||||
|
||||
// 清理
|
||||
this.activeAnimations.delete(animId);
|
||||
|
||||
resolve();
|
||||
};
|
||||
|
||||
// 动画取消处理
|
||||
anim.oncancel = () => {
|
||||
this.activeAnimations.delete(animId);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用进入动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
enter(element, animationName, options = {}) {
|
||||
const animation = this.get(animationName);
|
||||
if (!animation) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.apply(element, animationName, {
|
||||
...options,
|
||||
direction: 'normal',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用离开动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
leave(element, animationName, options = {}) {
|
||||
const animation = this.get(animationName);
|
||||
if (!animation) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.apply(element, animationName, {
|
||||
...options,
|
||||
direction: 'reverse',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有动画
|
||||
*/
|
||||
cancelAll() {
|
||||
this.activeAnimations.forEach(({ animation }) => {
|
||||
animation.cancel();
|
||||
});
|
||||
|
||||
this.activeAnimations.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
cancel(element) {
|
||||
this.activeAnimations.forEach(({ element: el, animation }, id) => {
|
||||
if (el === element) {
|
||||
animation.cancel();
|
||||
this.activeAnimations.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停所有动画
|
||||
*/
|
||||
pauseAll() {
|
||||
this.activeAnimations.forEach(({ animation }) => {
|
||||
animation.pause();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复所有动画
|
||||
*/
|
||||
resumeAll() {
|
||||
this.activeAnimations.forEach(({ animation }) => {
|
||||
animation.play();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
pause(element) {
|
||||
this.activeAnimations.forEach(({ element: el, animation }) => {
|
||||
if (el === element) {
|
||||
animation.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
resume(element) {
|
||||
this.activeAnimations.forEach(({ element: el, animation }) => {
|
||||
if (el === element) {
|
||||
animation.play();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动动画数量
|
||||
* @returns {number} 动画数量
|
||||
*/
|
||||
getActiveCount() {
|
||||
return this.activeAnimations.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有活动动画
|
||||
* @returns {Map} 活动动画映射
|
||||
*/
|
||||
getActiveAnimations() {
|
||||
return new Map(this.activeAnimations);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查元素是否有活动动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @returns {boolean} 是否有活动动画
|
||||
*/
|
||||
hasActiveAnimation(element) {
|
||||
for (const { element: el } of this.activeAnimations.values()) {
|
||||
if (el === element) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @returns {Object} 动画配置
|
||||
*/
|
||||
getConfig() {
|
||||
return {
|
||||
animations: Array.from(this.animations.keys()),
|
||||
activeCount: this.getActiveCount(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置动画管理器
|
||||
*/
|
||||
reset() {
|
||||
this.cancelAll();
|
||||
this.animations.clear();
|
||||
this._registerDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁动画管理器
|
||||
*/
|
||||
destroy() {
|
||||
this.cancelAll();
|
||||
this.animations.clear();
|
||||
this.activeAnimations.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预设动画效果
|
||||
*/
|
||||
const presetAnimations = {
|
||||
bounce: {
|
||||
enter: { transform: 'translateY(-80px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(20px)', opacity: 1 },
|
||||
duration: 650,
|
||||
easing: 'ease',
|
||||
},
|
||||
slideUp: {
|
||||
enter: { transform: 'translateY(60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideDown: {
|
||||
enter: { transform: 'translateY(-60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideLeft: {
|
||||
enter: { transform: 'translateX(-90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideRight: {
|
||||
enter: { transform: 'translateX(90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// 注册预设动画
|
||||
Object.entries(presetAnimations).forEach(([name, config]) => {
|
||||
ANIMATIONS[name] = config;
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建动画管理器实例
|
||||
* @returns {AnimationManager} 动画管理器实例
|
||||
*/
|
||||
export const createAnimationManager = () => {
|
||||
return new AnimationManager();
|
||||
};
|
||||
|
||||
// 创建默认实例
|
||||
const defaultAnimationManager = createAnimationManager();
|
||||
|
||||
/**
|
||||
* 动画工具函数
|
||||
*/
|
||||
export const animationUtils = {
|
||||
/**
|
||||
* 应用动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
apply(element, animationName, options = {}) {
|
||||
return defaultAnimationManager.apply(element, animationName, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用进入动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
enter(element, animationName, options = {}) {
|
||||
return defaultAnimationManager.enter(element, animationName, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用离开动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
leave(element, animationName, options = {}) {
|
||||
return defaultAnimationManager.leave(element, animationName, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消所有动画
|
||||
*/
|
||||
cancelAll() {
|
||||
defaultAnimationManager.cancelAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
cancel(element) {
|
||||
defaultAnimationManager.cancel(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂停所有动画
|
||||
*/
|
||||
pauseAll() {
|
||||
defaultAnimationManager.pauseAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复所有动画
|
||||
*/
|
||||
resumeAll() {
|
||||
defaultAnimationManager.resumeAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂停元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
pause(element) {
|
||||
defaultAnimationManager.pause(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
resume(element) {
|
||||
defaultAnimationManager.resume(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取活动动画数量
|
||||
* @returns {number} 动画数量
|
||||
*/
|
||||
getActiveCount() {
|
||||
return defaultAnimationManager.getActiveCount();
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查元素是否有活动动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @returns {boolean} 是否有活动动画
|
||||
*/
|
||||
hasActiveAnimation(element) {
|
||||
return defaultAnimationManager.hasActiveAnimation(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注册动画
|
||||
* @param {string} name - 动画名称
|
||||
* @param {Object} config - 动画配置
|
||||
*/
|
||||
register(name, config) {
|
||||
defaultAnimationManager.register(name, config);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注销动画
|
||||
* @param {string} name - 动画名称
|
||||
*/
|
||||
unregister(name) {
|
||||
defaultAnimationManager.unregister(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @param {string} name - 动画名称
|
||||
* @returns {Object|null} 动画配置
|
||||
*/
|
||||
get(name) {
|
||||
return defaultAnimationManager.get(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有动画名称
|
||||
* @returns {Array} 动画名称数组
|
||||
*/
|
||||
getAnimationNames() {
|
||||
return Array.from(defaultAnimationManager.animations.keys());
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @returns {Object} 动画配置
|
||||
*/
|
||||
getConfig() {
|
||||
return defaultAnimationManager.getConfig();
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置动画管理器
|
||||
*/
|
||||
reset() {
|
||||
defaultAnimationManager.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* 销毁动画管理器
|
||||
*/
|
||||
destroy() {
|
||||
defaultAnimationManager.destroy();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画预设
|
||||
*/
|
||||
export const animationPresets = presetAnimations;
|
||||
|
||||
/**
|
||||
* 创建自定义动画
|
||||
* @param {Object} config - 动画配置
|
||||
* @returns {Object} 动画配置
|
||||
*/
|
||||
export const createAnimation = (config) => {
|
||||
return {
|
||||
enter: config.enter || {},
|
||||
leave: config.leave || {},
|
||||
duration: config.duration || 300,
|
||||
easing: config.easing || 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
delay: config.delay || 0,
|
||||
iterations: config.iterations || 1,
|
||||
direction: config.direction || 'normal',
|
||||
fillMode: config.fillMode || 'forwards',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 组合动画
|
||||
* @param {Array} animations - 动画数组
|
||||
* @returns {Object} 组合后的动画配置
|
||||
*/
|
||||
export const combineAnimations = (animations) => {
|
||||
const combined = {
|
||||
enter: {},
|
||||
leave: {},
|
||||
duration: 0,
|
||||
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
delay: 0,
|
||||
iterations: 1,
|
||||
direction: 'normal',
|
||||
fillMode: 'forwards',
|
||||
};
|
||||
|
||||
animations.forEach((anim) => {
|
||||
if (anim.enter) {
|
||||
Object.assign(combined.enter, anim.enter);
|
||||
}
|
||||
if (anim.leave) {
|
||||
Object.assign(combined.leave, anim.leave);
|
||||
}
|
||||
if (anim.duration) {
|
||||
combined.duration = Math.max(combined.duration, anim.duration);
|
||||
}
|
||||
if (anim.easing) {
|
||||
combined.easing = anim.easing;
|
||||
}
|
||||
if (anim.delay) {
|
||||
combined.delay = Math.max(combined.delay, anim.delay);
|
||||
}
|
||||
});
|
||||
|
||||
return combined;
|
||||
};
|
||||
|
||||
/**
|
||||
* 链式动画
|
||||
* @param {Array} animations - 动画数组
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
export const chainAnimations = async (element, animations) => {
|
||||
for (const anim of animations) {
|
||||
await defaultAnimationManager.apply(element, anim.name, anim.options);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 并行动画
|
||||
* @param {Array} animations - 动画数组
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
export const parallelAnimations = (element, animations) => {
|
||||
return Promise.all(
|
||||
animations.map((anim) =>
|
||||
defaultAnimationManager.apply(element, anim.name, anim.options)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 延迟动画
|
||||
* @param {number} delay - 延迟时间(毫秒)
|
||||
* @returns {Promise} Promise对象
|
||||
*/
|
||||
export const delayAnimation = (delay) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, delay));
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画队列
|
||||
*/
|
||||
export class AnimationQueue {
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
this.isProcessing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加动画到队列
|
||||
* @param {Function} animationFn - 动画函数
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
add(animationFn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.queue.push({
|
||||
fn: animationFn,
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
|
||||
this._process();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理队列
|
||||
*/
|
||||
async _process() {
|
||||
if (this.isProcessing || this.queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProcessing = true;
|
||||
|
||||
while (this.queue.length > 0) {
|
||||
const { fn, resolve, reject } = this.queue.shift();
|
||||
|
||||
try {
|
||||
const result = await fn();
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
this.isProcessing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空队列
|
||||
*/
|
||||
clear() {
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停队列
|
||||
*/
|
||||
pause() {
|
||||
this.isProcessing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复队列
|
||||
*/
|
||||
resume() {
|
||||
this.isProcessing = false;
|
||||
this._process();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列长度
|
||||
* @returns {number} 队列长度
|
||||
*/
|
||||
get length() {
|
||||
return this.queue.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否正在处理
|
||||
* @returns {boolean} 是否正在处理
|
||||
*/
|
||||
get processing() {
|
||||
return this.isProcessing;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建动画队列
|
||||
* @returns {AnimationQueue} 动画队列实例
|
||||
*/
|
||||
export const createAnimationQueue = () => {
|
||||
return new AnimationQueue();
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画性能监控
|
||||
*/
|
||||
export class AnimationPerformanceMonitor {
|
||||
constructor() {
|
||||
this.metrics = {
|
||||
totalAnimations: 0,
|
||||
activeAnimations: 0,
|
||||
averageDuration: 0,
|
||||
maxDuration: 0,
|
||||
minDuration: Infinity,
|
||||
};
|
||||
|
||||
this.history = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录动画开始
|
||||
* @param {string} animationName - 动画名称
|
||||
*/
|
||||
recordStart(animationName) {
|
||||
this.metrics.totalAnimations++;
|
||||
this.metrics.activeAnimations++;
|
||||
|
||||
this.history.push({
|
||||
name: animationName,
|
||||
startTime: performance.now(),
|
||||
endTime: null,
|
||||
duration: null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录动画结束
|
||||
* @param {string} animationName - 动画名称
|
||||
*/
|
||||
recordEnd(animationName) {
|
||||
this.metrics.activeAnimations--;
|
||||
|
||||
const entry = this.history.find(
|
||||
(h) => h.name === animationName && h.endTime === null
|
||||
);
|
||||
|
||||
if (entry) {
|
||||
entry.endTime = performance.now();
|
||||
entry.duration = entry.endTime - entry.startTime;
|
||||
|
||||
// 更新统计
|
||||
this.metrics.maxDuration = Math.max(this.metrics.maxDuration, entry.duration);
|
||||
this.metrics.minDuration = Math.min(this.metrics.minDuration, entry.duration);
|
||||
|
||||
// 计算平均值
|
||||
const completed = this.history.filter((h) => h.duration !== null);
|
||||
this.metrics.averageDuration =
|
||||
completed.reduce((sum, h) => sum + h.duration, 0) / completed.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指标
|
||||
* @returns {Object} 性能指标
|
||||
*/
|
||||
getMetrics() {
|
||||
return { ...this.metrics };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
* @returns {Array} 历史记录
|
||||
*/
|
||||
getHistory() {
|
||||
return [...this.history];
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置监控
|
||||
*/
|
||||
reset() {
|
||||
this.metrics = {
|
||||
totalAnimations: 0,
|
||||
activeAnimations: 0,
|
||||
averageDuration: 0,
|
||||
maxDuration: 0,
|
||||
minDuration: Infinity,
|
||||
};
|
||||
|
||||
this.history = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建性能监控实例
|
||||
* @returns {AnimationPerformanceMonitor} 性能监控实例
|
||||
*/
|
||||
export const createPerformanceMonitor = () => {
|
||||
return new AnimationPerformanceMonitor();
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画缓存管理
|
||||
*/
|
||||
export const animationCacheManager = {
|
||||
/**
|
||||
* 缓存动画
|
||||
* @param {string} key - 缓存键
|
||||
* @param {Object} animation - 动画配置
|
||||
*/
|
||||
set(key, animation) {
|
||||
animationCache.set(key, animation);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取缓存动画
|
||||
* @param {string} key - 缓存键
|
||||
* @returns {Object|null} 动画配置
|
||||
*/
|
||||
get(key) {
|
||||
return animationCache.get(key) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查缓存
|
||||
* @param {string} key - 缓存键
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
has(key) {
|
||||
return animationCache.has(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param {string} key - 缓存键
|
||||
*/
|
||||
delete(key) {
|
||||
animationCache.delete(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
*/
|
||||
clear() {
|
||||
animationCache.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取缓存大小
|
||||
* @returns {number} 缓存大小
|
||||
*/
|
||||
size() {
|
||||
return animationCache.size;
|
||||
},
|
||||
};
|
||||
|
||||
export { AnimationManager, defaultAnimationManager };
|
||||
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* MetonaToast Constants - 常量定义
|
||||
* @module constants
|
||||
* @version 2.0.0
|
||||
* @description 默认配置、颜色、动画、主题等常量
|
||||
*/
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
export const DEFAULTS = Object.freeze({
|
||||
// 基础配置
|
||||
position: 'top-right',
|
||||
duration: 4000,
|
||||
max: 6,
|
||||
gap: 12,
|
||||
offset: 24,
|
||||
|
||||
// 交互配置
|
||||
pauseOnHover: true,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
|
||||
// 显示配置
|
||||
showProgress: true,
|
||||
progressDirection: 'horizontal',
|
||||
icon: true,
|
||||
closeButton: true,
|
||||
|
||||
// 主题配置
|
||||
theme: 'auto',
|
||||
animation: 'slide',
|
||||
|
||||
// 布局配置
|
||||
zIndex: 9999,
|
||||
width: 360,
|
||||
|
||||
// 自定义配置
|
||||
className: '',
|
||||
style: {},
|
||||
|
||||
// 回调函数
|
||||
onShow: null,
|
||||
onClose: null,
|
||||
onClick: null,
|
||||
|
||||
// 国际化
|
||||
locale: 'zh-CN',
|
||||
|
||||
// 插件
|
||||
plugins: [],
|
||||
});
|
||||
|
||||
// Icons — 从 icons.js 导入并重新导出(保持向后兼容)
|
||||
import { ICONS } from './icons.js';
|
||||
export { ICONS };
|
||||
|
||||
/**
|
||||
* 类型颜色配置
|
||||
*/
|
||||
export const TYPE_COLORS = {
|
||||
success: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
error: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
warning: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
info: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
loading: { fg: '#6366f1', bg: 'rgba(99, 102, 241, 0.1)' },
|
||||
default: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
check: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
x: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
alert: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
question: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
star: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
heart: { fg: '#ec4899', bg: 'rgba(236, 72, 153, 0.1)' },
|
||||
bell: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
mail: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
settings: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
user: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
home: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
search: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
plus: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
minus: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
edit: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
trash: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
download: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
upload: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
share: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
link: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
external: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
clock: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
calendar: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
map: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
compass: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
globe: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
wifi: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
cloud: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
sun: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
moon: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
zap: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
activity: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
cpu: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
database: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
server: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
terminal: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
code: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
git: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
package: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
layers: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
grid: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
list: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
filter: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
sort: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
refresh: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
sync: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
power: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
battery: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
bluetooth: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
volume: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
mic: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
camera: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
image: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
video: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
music: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
file: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
folder: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
clipboard: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
save: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
print: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
eye: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
eyeOff: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
lock: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
unlock: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
shield: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
key: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
flag: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
bookmark: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
tag: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
gift: { fg: '#ec4899', bg: 'rgba(236, 72, 153, 0.1)' },
|
||||
award: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
target: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
crosshair: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
move: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
maximize: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
minimize: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
copy: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
cut: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
paste: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
rotateCw: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
rotateCcw: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
zoomIn: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
zoomOut: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
crop: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
sliders: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
toggleLeft: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
toggleRight: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
checkCircle: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
xCircle: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
alertCircle: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
infoCircle: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
helpCircle: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
alertTriangle: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
checkSquare: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
square: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
circle: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
triangle: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
hexagon: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
octagon: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
pentagon: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
diamond: { fg: '#ec4899', bg: 'rgba(236, 72, 153, 0.1)' },
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画配置
|
||||
*/
|
||||
export const ANIMATIONS = {
|
||||
slide: {
|
||||
enter: { transform: 'translateX(80px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
fade: {
|
||||
enter: { opacity: 0, filter: 'blur(3px)' },
|
||||
leave: { opacity: 0, filter: 'blur(3px)' },
|
||||
duration: 500,
|
||||
easing: 'ease',
|
||||
},
|
||||
scale: {
|
||||
enter: { transform: 'scale(0.55)', opacity: 0 },
|
||||
leave: { transform: 'scale(0.55)', opacity: 0 },
|
||||
duration: 450,
|
||||
easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||
},
|
||||
bounce: {
|
||||
enter: { transform: 'translateY(-80px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(20px)', opacity: 0 },
|
||||
duration: 650,
|
||||
easing: 'ease',
|
||||
},
|
||||
flip: {
|
||||
enter: { transform: 'perspective(500px) rotateX(-90deg)', opacity: 0 },
|
||||
leave: { transform: 'perspective(500px) rotateX(90deg)', opacity: 0 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
rotate: {
|
||||
enter: { transform: 'rotate(-25deg) scale(0.6)', opacity: 0 },
|
||||
leave: { transform: 'rotate(25deg) scale(0.6)', opacity: 0 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
zoom: {
|
||||
enter: { transform: 'scale(0.1)', opacity: 0 },
|
||||
leave: { transform: 'scale(0.1)', opacity: 0 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||
},
|
||||
slideUp: {
|
||||
enter: { transform: 'translateY(60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(-120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideDown: {
|
||||
enter: { transform: 'translateY(-60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideLeft: {
|
||||
enter: { transform: 'translateX(-90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideRight: {
|
||||
enter: { transform: 'translateX(90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(-120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 主题配置
|
||||
*/
|
||||
export const THEMES = {
|
||||
light: {
|
||||
bg: 'rgba(255, 255, 255, 0.96)',
|
||||
text: '#1f2937',
|
||||
border: 'rgba(0, 0, 0, 0.06)',
|
||||
shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.18), 0 4px 14px -4px rgba(0, 0, 0, 0.08)',
|
||||
hoverShadow: '0 14px 48px -10px rgba(0, 0, 0, 0.22), 0 6px 18px -4px rgba(0, 0, 0, 0.10)',
|
||||
progressBg: 'rgba(0, 0, 0, 0.06)',
|
||||
closeHoverBg: 'rgba(0, 0, 0, 0.06)',
|
||||
},
|
||||
dark: {
|
||||
bg: 'rgba(28, 32, 40, 0.94)',
|
||||
text: '#e6e8eb',
|
||||
border: 'rgba(255, 255, 255, 0.08)',
|
||||
shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.6), 0 4px 14px -4px rgba(0, 0, 0, 0.4)',
|
||||
hoverShadow: '0 14px 48px -8px rgba(0, 0, 0, 0.6), 0 6px 18px -4px rgba(0, 0, 0, 0.4)',
|
||||
progressBg: 'rgba(255, 255, 255, 0.08)',
|
||||
closeHoverBg: 'rgba(255, 255, 255, 0.08)',
|
||||
},
|
||||
auto: 'auto',
|
||||
warm: {
|
||||
bg: 'rgba(255, 251, 235, 0.96)',
|
||||
text: '#78350f',
|
||||
border: 'rgba(245, 158, 11, 0.2)',
|
||||
shadow: '0 10px 36px -10px rgba(245, 158, 11, 0.18), 0 4px 14px -4px rgba(245, 158, 11, 0.08)',
|
||||
hoverShadow: '0 14px 48px -10px rgba(245, 158, 11, 0.22), 0 6px 18px -4px rgba(245, 158, 11, 0.10)',
|
||||
progressBg: 'rgba(245, 158, 11, 0.1)',
|
||||
closeHoverBg: 'rgba(245, 158, 11, 0.1)',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 位置配置
|
||||
*/
|
||||
export const POSITIONS = [
|
||||
'top-left',
|
||||
'top-center',
|
||||
'top-right',
|
||||
'bottom-left',
|
||||
'bottom-center',
|
||||
'bottom-right',
|
||||
];
|
||||
|
||||
/**
|
||||
* 类型配置
|
||||
*/
|
||||
export const TYPES = [
|
||||
'default', 'success', 'error', 'warning', 'info', 'loading',
|
||||
'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',
|
||||
];
|
||||
|
||||
// Locales — 从 locales.js 导入并重新导出(保持向后兼容)
|
||||
import { LOCALES } from './locales.js';
|
||||
export { LOCALES };
|
||||
|
||||
/**
|
||||
* 进度条方向
|
||||
*/
|
||||
export const PROGRESS_DIRECTIONS = ['horizontal', 'vertical'];
|
||||
|
||||
/**
|
||||
* 动画类型
|
||||
*/
|
||||
export const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', 'zoom', 'slideUp', 'slideDown', 'slideLeft', 'slideRight'];
|
||||
|
||||
/**
|
||||
* 主题类型
|
||||
*/
|
||||
export const THEME_TYPES = ['light', 'dark', 'auto'];
|
||||
+1070
File diff suppressed because it is too large
Load Diff
+1054
File diff suppressed because it is too large
Load Diff
+603
@@ -0,0 +1,603 @@
|
||||
/**
|
||||
* MetonaToast Icons — 图标SVG定义
|
||||
* @module icons
|
||||
* @version 2.0.0
|
||||
* @description 80+ 内置SVG图标
|
||||
*/
|
||||
|
||||
export const ICONS = {
|
||||
success: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>`,
|
||||
|
||||
error: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="15" y1="9" x2="9" y2="15"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
warning: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
info: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||
</svg>`,
|
||||
|
||||
loading: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56" class="met-spin"/>
|
||||
</svg>`,
|
||||
|
||||
close: `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
check: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>`,
|
||||
|
||||
x: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
alert: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
question: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
star: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||||
</svg>`,
|
||||
|
||||
heart: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
|
||||
</svg>`,
|
||||
|
||||
bell: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
||||
</svg>`,
|
||||
|
||||
mail: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>`,
|
||||
|
||||
settings: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
</svg>`,
|
||||
|
||||
user: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>`,
|
||||
|
||||
home: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
<polyline points="9 22 9 12 15 12 15 22"/>
|
||||
</svg>`,
|
||||
|
||||
search: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
</svg>`,
|
||||
|
||||
plus: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
minus: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
edit: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||||
</svg>`,
|
||||
|
||||
trash: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
download: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" y1="15" x2="12" y2="3"/>
|
||||
</svg>`,
|
||||
|
||||
upload: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="17 8 12 3 7 8"/>
|
||||
<line x1="12" y1="3" x2="12" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
share: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="18" cy="5" r="3"/>
|
||||
<circle cx="6" cy="12" r="3"/>
|
||||
<circle cx="18" cy="19" r="3"/>
|
||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
|
||||
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
||||
</svg>`,
|
||||
|
||||
link: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||
</svg>`,
|
||||
|
||||
external: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" y1="14" x2="21" y2="3"/>
|
||||
</svg>`,
|
||||
|
||||
clock: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>`,
|
||||
|
||||
calendar: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="16" y1="2" x2="16" y2="6"/>
|
||||
<line x1="8" y1="2" x2="8" y2="6"/>
|
||||
<line x1="3" y1="10" x2="21" y2="10"/>
|
||||
</svg>`,
|
||||
|
||||
map: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/>
|
||||
<line x1="8" y1="2" x2="8" y2="18"/>
|
||||
<line x1="16" y1="6" x2="16" y2="22"/>
|
||||
</svg>`,
|
||||
|
||||
compass: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/>
|
||||
</svg>`,
|
||||
|
||||
globe: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="2" y1="12" x2="22" y2="12"/>
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
|
||||
</svg>`,
|
||||
|
||||
wifi: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
|
||||
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
|
||||
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
|
||||
<line x1="12" y1="20" x2="12.01" y2="20"/>
|
||||
</svg>`,
|
||||
|
||||
cloud: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/>
|
||||
</svg>`,
|
||||
|
||||
sun: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"/>
|
||||
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||
<line x1="12" y1="21" x2="12" y2="23"/>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||
<line x1="1" y1="12" x2="3" y2="12"/>
|
||||
<line x1="21" y1="12" x2="23" y2="12"/>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||
</svg>`,
|
||||
|
||||
moon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||
</svg>`,
|
||||
|
||||
zap: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
||||
</svg>`,
|
||||
|
||||
activity: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
|
||||
</svg>`,
|
||||
|
||||
cpu: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"/>
|
||||
<rect x="9" y="9" width="6" height="6"/>
|
||||
<line x1="9" y1="1" x2="9" y2="4"/>
|
||||
<line x1="15" y1="1" x2="15" y2="4"/>
|
||||
<line x1="9" y1="20" x2="9" y2="23"/>
|
||||
<line x1="15" y1="20" x2="15" y2="23"/>
|
||||
<line x1="20" y1="9" x2="23" y2="9"/>
|
||||
<line x1="20" y1="14" x2="23" y2="14"/>
|
||||
<line x1="1" y1="9" x2="4" y2="9"/>
|
||||
<line x1="1" y1="14" x2="4" y2="14"/>
|
||||
</svg>`,
|
||||
|
||||
database: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<ellipse cx="12" cy="5" rx="9" ry="3"/>
|
||||
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
|
||||
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
||||
</svg>`,
|
||||
|
||||
server: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/>
|
||||
<rect x="2" y="14" width="20" height="8" rx="2" ry="2"/>
|
||||
<line x1="6" y1="6" x2="6.01" y2="6"/>
|
||||
<line x1="6" y1="18" x2="6.01" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
terminal: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="4 17 10 11 4 5"/>
|
||||
<line x1="12" y1="19" x2="20" y2="19"/>
|
||||
</svg>`,
|
||||
|
||||
code: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="16 18 22 12 16 6"/>
|
||||
<polyline points="8 6 2 12 8 18"/>
|
||||
</svg>`,
|
||||
|
||||
git: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="18" cy="18" r="3"/>
|
||||
<circle cx="6" cy="6" r="3"/>
|
||||
<path d="M13 6h3a2 2 0 0 1 2 2v7"/>
|
||||
<line x1="6" y1="9" x2="6" y2="21"/>
|
||||
</svg>`,
|
||||
|
||||
package: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"/>
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
layers: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 2 7 12 12 22 7 12 2"/>
|
||||
<polyline points="2 17 12 22 22 17"/>
|
||||
<polyline points="2 12 12 17 22 12"/>
|
||||
</svg>`,
|
||||
|
||||
grid: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/>
|
||||
<rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>`,
|
||||
|
||||
list: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="8" y1="6" x2="21" y2="6"/>
|
||||
<line x1="8" y1="12" x2="21" y2="12"/>
|
||||
<line x1="8" y1="18" x2="21" y2="18"/>
|
||||
<line x1="3" y1="6" x2="3.01" y2="6"/>
|
||||
<line x1="3" y1="12" x2="3.01" y2="12"/>
|
||||
<line x1="3" y1="18" x2="3.01" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
filter: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
|
||||
</svg>`,
|
||||
|
||||
sort: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<polyline points="19 12 12 19 5 12"/>
|
||||
</svg>`,
|
||||
|
||||
refresh: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>`,
|
||||
|
||||
sync: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>`,
|
||||
|
||||
power: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>
|
||||
<line x1="12" y1="2" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
battery: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="6" width="18" height="12" rx="2" ry="2"/>
|
||||
<line x1="23" y1="13" x2="23" y2="11"/>
|
||||
</svg>`,
|
||||
|
||||
bluetooth: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6.5 6.5 17.5 17.5 12 23 12 1 17.5 6.5 6.5 17.5"/>
|
||||
</svg>`,
|
||||
|
||||
volume: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
<path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/>
|
||||
</svg>`,
|
||||
|
||||
mic: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
||||
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
||||
<line x1="12" y1="19" x2="12" y2="23"/>
|
||||
<line x1="8" y1="23" x2="16" y2="23"/>
|
||||
</svg>`,
|
||||
|
||||
camera: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
|
||||
<circle cx="12" cy="13" r="4"/>
|
||||
</svg>`,
|
||||
|
||||
image: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||||
<polyline points="21 15 16 10 5 21"/>
|
||||
</svg>`,
|
||||
|
||||
video: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="23 7 16 12 23 17 23 7"/>
|
||||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||||
</svg>`,
|
||||
|
||||
music: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M9 18V5l12-2v13"/>
|
||||
<circle cx="6" cy="18" r="3"/>
|
||||
<circle cx="18" cy="16" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
file: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
|
||||
<polyline points="13 2 13 9 20 9"/>
|
||||
</svg>`,
|
||||
|
||||
folder: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
|
||||
</svg>`,
|
||||
|
||||
clipboard: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
|
||||
</svg>`,
|
||||
|
||||
save: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
||||
<polyline points="17 21 17 13 7 13 7 21"/>
|
||||
<polyline points="7 3 7 8 15 8"/>
|
||||
</svg>`,
|
||||
|
||||
print: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 6 2 18 2 18 9"/>
|
||||
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/>
|
||||
<rect x="6" y="14" width="12" height="8"/>
|
||||
</svg>`,
|
||||
|
||||
eye: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
eyeOff: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/>
|
||||
<line x1="1" y1="1" x2="23" y2="23"/>
|
||||
</svg>`,
|
||||
|
||||
lock: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
</svg>`,
|
||||
|
||||
unlock: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 9.9-1"/>
|
||||
</svg>`,
|
||||
|
||||
shield: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>`,
|
||||
|
||||
key: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/>
|
||||
</svg>`,
|
||||
|
||||
flag: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/>
|
||||
<line x1="4" y1="22" x2="4" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
bookmark: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/>
|
||||
</svg>`,
|
||||
|
||||
tag: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/>
|
||||
<line x1="7" y1="7" x2="7.01" y2="7"/>
|
||||
</svg>`,
|
||||
|
||||
gift: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 12 20 22 4 22 4 12"/>
|
||||
<rect x="2" y="7" width="20" height="5"/>
|
||||
<line x1="12" y1="22" x2="12" y2="7"/>
|
||||
<path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"/>
|
||||
<path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"/>
|
||||
</svg>`,
|
||||
|
||||
award: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="8" r="7"/>
|
||||
<polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"/>
|
||||
</svg>`,
|
||||
|
||||
target: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<circle cx="12" cy="12" r="6"/>
|
||||
<circle cx="12" cy="12" r="2"/>
|
||||
</svg>`,
|
||||
|
||||
crosshair: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="22" y1="12" x2="18" y2="12"/>
|
||||
<line x1="6" y1="12" x2="2" y2="12"/>
|
||||
<line x1="12" y1="6" x2="12" y2="2"/>
|
||||
<line x1="12" y1="22" x2="12" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
move: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="5 9 2 12 5 15"/>
|
||||
<polyline points="9 5 12 2 15 5"/>
|
||||
<polyline points="15 19 12 22 9 19"/>
|
||||
<polyline points="19 9 22 12 19 15"/>
|
||||
<line x1="2" y1="12" x2="22" y2="12"/>
|
||||
<line x1="12" y1="2" x2="12" y2="22"/>
|
||||
</svg>`,
|
||||
|
||||
maximize: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
||||
</svg>`,
|
||||
|
||||
minimize: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
|
||||
</svg>`,
|
||||
|
||||
copy: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg>`,
|
||||
|
||||
cut: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="6" cy="6" r="3"/>
|
||||
<circle cx="6" cy="18" r="3"/>
|
||||
<line x1="20" y1="4" x2="8.12" y2="15.88"/>
|
||||
<line x1="14.47" y1="14.48" x2="20" y2="20"/>
|
||||
<line x1="8.12" y1="8.12" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
paste: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
|
||||
</svg>`,
|
||||
|
||||
rotateCw: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||||
</svg>`,
|
||||
|
||||
rotateCcw: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>`,
|
||||
|
||||
zoomIn: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
<line x1="11" y1="8" x2="11" y2="14"/>
|
||||
<line x1="8" y1="11" x2="14" y2="11"/>
|
||||
</svg>`,
|
||||
|
||||
zoomOut: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
<line x1="8" y1="11" x2="14" y2="11"/>
|
||||
</svg>`,
|
||||
|
||||
crop: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6.13 1L6 16a2 2 0 0 0 2 2h15"/>
|
||||
<path d="M1 6.13L16 6a2 2 0 0 1 2 2v15"/>
|
||||
</svg>`,
|
||||
|
||||
sliders: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="4" y1="21" x2="4" y2="14"/>
|
||||
<line x1="4" y1="10" x2="4" y2="3"/>
|
||||
<line x1="12" y1="21" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12" y2="3"/>
|
||||
<line x1="20" y1="21" x2="20" y2="16"/>
|
||||
<line x1="20" y1="12" x2="20" y2="3"/>
|
||||
<line x1="1" y1="14" x2="7" y2="14"/>
|
||||
<line x1="9" y1="8" x2="15" y2="8"/>
|
||||
<line x1="17" y1="16" x2="23" y2="16"/>
|
||||
</svg>`,
|
||||
|
||||
toggleLeft: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="5" width="22" height="14" rx="7" ry="7"/>
|
||||
<circle cx="8" cy="12" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
toggleRight: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="5" width="22" height="14" rx="7" ry="7"/>
|
||||
<circle cx="16" cy="12" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
checkCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
||||
<polyline points="22 4 12 14.01 9 11.01"/>
|
||||
</svg>`,
|
||||
|
||||
xCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="15" y1="9" x2="9" y2="15"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
alertCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="8" x2="12" y2="12"/>
|
||||
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||
</svg>`,
|
||||
|
||||
infoCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||
</svg>`,
|
||||
|
||||
helpCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
alertTriangle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
checkSquare: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 11 12 14 22 4"/>
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
||||
</svg>`,
|
||||
|
||||
square: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
</svg>`,
|
||||
|
||||
circle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
</svg>`,
|
||||
|
||||
triangle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
</svg>`,
|
||||
|
||||
hexagon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
</svg>`,
|
||||
|
||||
octagon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"/>
|
||||
</svg>`,
|
||||
|
||||
pentagon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 22 8.5 19 20 5 20 2 8.5 12 2"/>
|
||||
</svg>`,
|
||||
|
||||
diamond: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4.5" y="4.5" width="15" height="15" rx="1" transform="rotate(45 12 12)"/>
|
||||
</svg>`,
|
||||
};
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* MetonaToast - 轻量级Toast通知库
|
||||
* @module metona-toast
|
||||
* @version 2.0.0
|
||||
* @author metona
|
||||
* @description 轻量、零依赖、精致美观的Toast通知库。单文件,开箱即用。
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { meToast, Toast } from './core.js';
|
||||
import { animationUtils } from './animations.js';
|
||||
import { themeUtils } from './themes.js';
|
||||
import { i18nUtils } from './i18n.js';
|
||||
import { pluginUtils, presetPlugins } from './plugins.js';
|
||||
import { DEFAULTS } from './constants.js';
|
||||
|
||||
// 版本信息
|
||||
const VERSION = '2.0.0';
|
||||
|
||||
/**
|
||||
* 主对象增强
|
||||
*/
|
||||
const enhancedMeToast = {
|
||||
...meToast,
|
||||
|
||||
version: VERSION,
|
||||
|
||||
animations: animationUtils,
|
||||
themes: themeUtils,
|
||||
i18n: i18nUtils,
|
||||
plugins: pluginUtils,
|
||||
presetPlugins,
|
||||
|
||||
/**
|
||||
* 安装插件,并连接必要的生命周期钩子
|
||||
*/
|
||||
use(plugin, options = {}) {
|
||||
if (typeof plugin === 'string') {
|
||||
const preset = presetPlugins[plugin];
|
||||
if (!preset) {
|
||||
console.warn(`Preset plugin "${plugin}" not found`);
|
||||
return this;
|
||||
}
|
||||
this.plugins.register(plugin, { ...preset, ...options });
|
||||
|
||||
// 连接插件钩子
|
||||
if (plugin === 'accessibility') {
|
||||
Toast.on('afterShow', (toast) => preset.announce(toast));
|
||||
}
|
||||
if (plugin === 'persistence') {
|
||||
const saved = preset.install();
|
||||
if (saved) this.configure(saved);
|
||||
Toast.on('afterClose', () => { preset.save(this.getConfig()); });
|
||||
}
|
||||
} else if (plugin && typeof plugin === 'object') {
|
||||
const name = plugin.name || 'custom';
|
||||
this.plugins.register(name, { ...plugin, ...options });
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init(options = {}) {
|
||||
if (options.config) {
|
||||
this.configure(options.config);
|
||||
}
|
||||
|
||||
if (options.theme) {
|
||||
this.themes.switchTheme(options.theme);
|
||||
}
|
||||
|
||||
if (options.locale) {
|
||||
this.i18n.switchLocale(options.locale);
|
||||
}
|
||||
|
||||
if (options.plugins && Array.isArray(options.plugins)) {
|
||||
options.plugins.forEach((plugin) => {
|
||||
this.use(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
destroy() {
|
||||
this.dismiss();
|
||||
|
||||
if (this.plugins && typeof this.plugins.destroy === 'function') {
|
||||
this.plugins.destroy();
|
||||
}
|
||||
|
||||
if (this.themes) {
|
||||
if (typeof this.themes.clearThemeListeners === 'function') {
|
||||
this.themes.clearThemeListeners();
|
||||
}
|
||||
if (typeof this.themes.unwatchSystemTheme === 'function') {
|
||||
this.themes.unwatchSystemTheme();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.i18n && typeof this.i18n.clearLocaleListeners === 'function') {
|
||||
this.i18n.clearLocaleListeners();
|
||||
}
|
||||
|
||||
if (this.animations && typeof this.animations.cancelAll === 'function') {
|
||||
this.animations.cancelAll();
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
const containers = document.querySelectorAll('.met-container');
|
||||
containers.forEach((c) => c.parentNode && c.parentNode.removeChild(c));
|
||||
|
||||
const style = document.getElementById('metona-toast-styles');
|
||||
if (style) style.parentNode.removeChild(style);
|
||||
}
|
||||
|
||||
this._toasts.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取状态
|
||||
*/
|
||||
getStatus() {
|
||||
return {
|
||||
version: VERSION,
|
||||
toasts: this._toasts.size,
|
||||
theme: this.themes?.getCurrentTheme?.() || 'auto',
|
||||
locale: this.i18n?.getCurrentLocale?.() || 'zh-CN',
|
||||
plugins: this.plugins?.getNames?.() || [],
|
||||
animations: this.animations?.getActiveCount?.() || 0,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*/
|
||||
getConfig() {
|
||||
return { ...this._config };
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
*/
|
||||
updateConfig(config) {
|
||||
if (config && typeof config === 'object') {
|
||||
this._config = { ...this._config, ...config };
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置配置
|
||||
*/
|
||||
resetConfig() {
|
||||
this._config = { ...DEFAULTS };
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取Toast列表
|
||||
*/
|
||||
getToasts() {
|
||||
return Array.from(this._toasts.values());
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查是否有Toast
|
||||
*/
|
||||
hasToasts() {
|
||||
return this._toasts.size > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取Toast
|
||||
*/
|
||||
getToast(id) {
|
||||
if (!id) return null;
|
||||
return this._toasts.get(id) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭所有Toast
|
||||
*/
|
||||
closeAll() {
|
||||
this._toasts.forEach((t) => t.close());
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除所有Toast
|
||||
*/
|
||||
clearAll() {
|
||||
this._toasts.forEach((t) => t.close());
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂停所有Toast
|
||||
*/
|
||||
pauseAll() {
|
||||
this._toasts.forEach((t) => t._pause());
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复所有Toast
|
||||
*/
|
||||
resumeAll() {
|
||||
this._toasts.forEach((t) => t._resume());
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新所有Toast
|
||||
*/
|
||||
updateAll(partial) {
|
||||
if (partial && typeof partial === 'object') {
|
||||
this._toasts.forEach((t) => t.update(partial));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 查找Toast
|
||||
*/
|
||||
findToasts(predicate) {
|
||||
if (typeof predicate !== 'function') return [];
|
||||
return Array.from(this._toasts.values()).filter(predicate);
|
||||
},
|
||||
|
||||
/**
|
||||
* 按类型查找Toast
|
||||
*/
|
||||
findByType(type) {
|
||||
return this.findToasts((t) => t.type === type);
|
||||
},
|
||||
|
||||
/**
|
||||
* 按位置查找Toast
|
||||
*/
|
||||
findByPosition(position) {
|
||||
return this.findToasts((t) => t.config.position === position);
|
||||
},
|
||||
|
||||
};
|
||||
// 初始化主题和国际化
|
||||
if (typeof themeUtils.initTheme === 'function') {
|
||||
themeUtils.initTheme();
|
||||
}
|
||||
if (typeof i18nUtils.initI18n === 'function') {
|
||||
i18nUtils.initI18n();
|
||||
}
|
||||
|
||||
// 浏览器环境全局注册
|
||||
if (typeof window !== 'undefined') {
|
||||
window.MeToast = enhancedMeToast;
|
||||
window.Met = enhancedMeToast;
|
||||
window.metonaToast = enhancedMeToast;
|
||||
}
|
||||
|
||||
// 导出
|
||||
export default enhancedMeToast;
|
||||
export { enhancedMeToast as meToast, enhancedMeToast as Met, enhancedMeToast as MeToast, Toast, VERSION };
|
||||
+752
@@ -0,0 +1,752 @@
|
||||
/**
|
||||
* MetonaToast Locales — 国际化翻译数据
|
||||
* @module locales
|
||||
* @version 2.0.0
|
||||
* @description 内置 zh-CN / en-US 完整翻译
|
||||
*/
|
||||
|
||||
export const LOCALES = {
|
||||
'zh-CN': {
|
||||
close: '关闭',
|
||||
loading: '加载中...',
|
||||
success: '操作成功',
|
||||
error: '操作失败',
|
||||
warning: '警告',
|
||||
info: '信息',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
yes: '是',
|
||||
no: '否',
|
||||
ok: '确定',
|
||||
retry: '重试',
|
||||
undo: '撤销',
|
||||
redo: '重做',
|
||||
save: '保存',
|
||||
delete: '删除',
|
||||
edit: '编辑',
|
||||
add: '添加',
|
||||
remove: '移除',
|
||||
search: '搜索',
|
||||
filter: '筛选',
|
||||
sort: '排序',
|
||||
refresh: '刷新',
|
||||
sync: '同步',
|
||||
upload: '上传',
|
||||
download: '下载',
|
||||
share: '分享',
|
||||
copy: '复制',
|
||||
cut: '剪切',
|
||||
paste: '粘贴',
|
||||
print: '打印',
|
||||
export: '导出',
|
||||
import: '导入',
|
||||
settings: '设置',
|
||||
help: '帮助',
|
||||
about: '关于',
|
||||
version: '版本',
|
||||
language: '语言',
|
||||
theme: '主题',
|
||||
dark: '暗色',
|
||||
light: '亮色',
|
||||
auto: '自动',
|
||||
notifications: '通知',
|
||||
enable: '启用',
|
||||
disable: '禁用',
|
||||
on: '开启',
|
||||
off: '关闭',
|
||||
max: '最大',
|
||||
min: '最小',
|
||||
expand: '展开',
|
||||
collapse: '折叠',
|
||||
fullscreen: '全屏',
|
||||
exitFullscreen: '退出全屏',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '缩小',
|
||||
reset: '重置',
|
||||
apply: '应用',
|
||||
submit: '提交',
|
||||
send: '发送',
|
||||
receive: '接收',
|
||||
connect: '连接',
|
||||
disconnect: '断开',
|
||||
start: '开始',
|
||||
stop: '停止',
|
||||
pause: '暂停',
|
||||
resume: '继续',
|
||||
next: '下一个',
|
||||
previous: '上一个',
|
||||
first: '第一个',
|
||||
last: '最后一个',
|
||||
back: '返回',
|
||||
forward: '前进',
|
||||
home: '首页',
|
||||
menu: '菜单',
|
||||
more: '更多',
|
||||
less: '更少',
|
||||
show: '显示',
|
||||
hide: '隐藏',
|
||||
visible: '可见',
|
||||
hidden: '隐藏',
|
||||
enabled: '已启用',
|
||||
disabled: '已禁用',
|
||||
active: '活跃',
|
||||
inactive: '非活跃',
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
connected: '已连接',
|
||||
disconnected: '已断开',
|
||||
loading: '加载中',
|
||||
loaded: '已加载',
|
||||
saving: '保存中',
|
||||
saved: '已保存',
|
||||
deleting: '删除中',
|
||||
deleted: '已删除',
|
||||
uploading: '上传中',
|
||||
uploaded: '已上传',
|
||||
downloading: '下载中',
|
||||
downloaded: '已下载',
|
||||
processing: '处理中',
|
||||
processed: '已处理',
|
||||
syncing: '同步中',
|
||||
synced: '已同步',
|
||||
sending: '发送中',
|
||||
sent: '已发送',
|
||||
receiving: '接收中',
|
||||
received: '已接收',
|
||||
connecting: '连接中',
|
||||
error: '错误',
|
||||
warning: '警告',
|
||||
info: '信息',
|
||||
success: '成功',
|
||||
question: '问题',
|
||||
help: '帮助',
|
||||
feedback: '反馈',
|
||||
report: '报告',
|
||||
bug: '错误',
|
||||
feature: '功能',
|
||||
improvement: '改进',
|
||||
suggestion: '建议',
|
||||
request: '请求',
|
||||
response: '响应',
|
||||
status: '状态',
|
||||
progress: '进度',
|
||||
complete: '完成',
|
||||
incomplete: '未完成',
|
||||
pending: '待处理',
|
||||
processing: '处理中',
|
||||
failed: '失败',
|
||||
cancelled: '已取消',
|
||||
timeout: '超时',
|
||||
expired: '已过期',
|
||||
invalid: '无效',
|
||||
valid: '有效',
|
||||
required: '必填',
|
||||
optional: '可选',
|
||||
enabled: '启用',
|
||||
disabled: '禁用',
|
||||
allowed: '允许',
|
||||
denied: '拒绝',
|
||||
approved: '已批准',
|
||||
rejected: '已拒绝',
|
||||
accepted: '已接受',
|
||||
declined: '已拒绝',
|
||||
confirmed: '已确认',
|
||||
unconfirmed: '未确认',
|
||||
verified: '已验证',
|
||||
unverified: '未验证',
|
||||
authenticated: '已认证',
|
||||
unauthenticated: '未认证',
|
||||
authorized: '已授权',
|
||||
unauthorized: '未授权',
|
||||
public: '公开',
|
||||
private: '私有',
|
||||
protected: '受保护',
|
||||
internal: '内部',
|
||||
external: '外部',
|
||||
local: '本地',
|
||||
remote: '远程',
|
||||
global: '全局',
|
||||
system: '系统',
|
||||
user: '用户',
|
||||
admin: '管理员',
|
||||
moderator: '版主',
|
||||
guest: '访客',
|
||||
member: '成员',
|
||||
owner: '所有者',
|
||||
creator: '创建者',
|
||||
editor: '编辑者',
|
||||
viewer: '查看者',
|
||||
subscriber: '订阅者',
|
||||
follower: '关注者',
|
||||
following: '关注中',
|
||||
friend: '朋友',
|
||||
contact: '联系人',
|
||||
group: '群组',
|
||||
team: '团队',
|
||||
organization: '组织',
|
||||
company: '公司',
|
||||
department: '部门',
|
||||
project: '项目',
|
||||
task: '任务',
|
||||
issue: '问题',
|
||||
ticket: '工单',
|
||||
request: '请求',
|
||||
response: '响应',
|
||||
message: '消息',
|
||||
notification: '通知',
|
||||
alert: '警报',
|
||||
reminder: '提醒',
|
||||
event: '事件',
|
||||
activity: '活动',
|
||||
log: '日志',
|
||||
report: '报告',
|
||||
analytics: '分析',
|
||||
statistics: '统计',
|
||||
metrics: '指标',
|
||||
performance: '性能',
|
||||
security: '安全',
|
||||
privacy: '隐私',
|
||||
compliance: '合规',
|
||||
policy: '政策',
|
||||
terms: '条款',
|
||||
conditions: '条件',
|
||||
agreement: '协议',
|
||||
contract: '合同',
|
||||
license: '许可证',
|
||||
copyright: '版权',
|
||||
trademark: '商标',
|
||||
patent: '专利',
|
||||
tradeSecret: '商业秘密',
|
||||
confidential: '机密',
|
||||
sensitive: '敏感',
|
||||
critical: '关键',
|
||||
important: '重要',
|
||||
urgent: '紧急',
|
||||
high: '高',
|
||||
medium: '中',
|
||||
low: '低',
|
||||
none: '无',
|
||||
all: '全部',
|
||||
any: '任何',
|
||||
some: '一些',
|
||||
each: '每个',
|
||||
every: '每个',
|
||||
both: '两者',
|
||||
neither: '两者都不',
|
||||
either: '任一',
|
||||
other: '其他',
|
||||
another: '另一个',
|
||||
this: '这个',
|
||||
that: '那个',
|
||||
these: '这些',
|
||||
those: '那些',
|
||||
here: '这里',
|
||||
there: '那里',
|
||||
where: '哪里',
|
||||
when: '何时',
|
||||
why: '为什么',
|
||||
how: '如何',
|
||||
what: '什么',
|
||||
who: '谁',
|
||||
which: '哪个',
|
||||
whose: '谁的',
|
||||
whom: '谁',
|
||||
myself: '我自己',
|
||||
yourself: '你自己',
|
||||
himself: '他自己',
|
||||
herself: '她自己',
|
||||
itself: '它自己',
|
||||
ourselves: '我们自己',
|
||||
yourselves: '你们自己',
|
||||
themselves: '他们自己',
|
||||
mine: '我的',
|
||||
yours: '你的',
|
||||
his: '他的',
|
||||
hers: '她的',
|
||||
its: '它的',
|
||||
ours: '我们的',
|
||||
yours: '你们的',
|
||||
theirs: '他们的',
|
||||
me: '我',
|
||||
you: '你',
|
||||
him: '他',
|
||||
her: '她',
|
||||
it: '它',
|
||||
us: '我们',
|
||||
them: '他们',
|
||||
my: '我的',
|
||||
your: '你的',
|
||||
his: '他的',
|
||||
her: '她的',
|
||||
its: '它的',
|
||||
our: '我们的',
|
||||
your: '你们的',
|
||||
their: '他们的',
|
||||
i: '我',
|
||||
you: '你',
|
||||
he: '他',
|
||||
she: '她',
|
||||
it: '它',
|
||||
we: '我们',
|
||||
they: '他们',
|
||||
am: '是',
|
||||
is: '是',
|
||||
are: '是',
|
||||
was: '是',
|
||||
were: '是',
|
||||
be: '是',
|
||||
been: '是',
|
||||
being: '是',
|
||||
have: '有',
|
||||
has: '有',
|
||||
had: '有',
|
||||
having: '有',
|
||||
do: '做',
|
||||
does: '做',
|
||||
did: '做',
|
||||
doing: '做',
|
||||
will: '会',
|
||||
would: '会',
|
||||
shall: '将',
|
||||
should: '应该',
|
||||
may: '可以',
|
||||
might: '可能',
|
||||
can: '能',
|
||||
could: '能',
|
||||
must: '必须',
|
||||
need: '需要',
|
||||
dare: '敢',
|
||||
ought: '应该',
|
||||
used: '过去常常',
|
||||
to: '到',
|
||||
of: '的',
|
||||
in: '在',
|
||||
for: '为了',
|
||||
on: '在',
|
||||
with: '和',
|
||||
at: '在',
|
||||
by: '通过',
|
||||
from: '从',
|
||||
into: '进入',
|
||||
during: '在...期间',
|
||||
before: '在...之前',
|
||||
after: '在...之后',
|
||||
above: '在...上面',
|
||||
below: '在...下面',
|
||||
between: '在...之间',
|
||||
under: '在...下面',
|
||||
over: '在...上面',
|
||||
across: '穿过',
|
||||
through: '通过',
|
||||
into: '进入',
|
||||
towards: '朝向',
|
||||
upon: '在...上面',
|
||||
about: '关于',
|
||||
against: '反对',
|
||||
among: '在...之中',
|
||||
along: '沿着',
|
||||
around: '周围',
|
||||
beyond: '超出',
|
||||
but: '但是',
|
||||
despite: '尽管',
|
||||
except: '除了',
|
||||
inside: '里面',
|
||||
outside: '外面',
|
||||
since: '自从',
|
||||
until: '直到',
|
||||
unless: '除非',
|
||||
whether: '是否',
|
||||
while: '当...时候',
|
||||
although: '虽然',
|
||||
because: '因为',
|
||||
if: '如果',
|
||||
once: '一旦',
|
||||
since: '自从',
|
||||
so: '所以',
|
||||
that: '那个',
|
||||
though: '虽然',
|
||||
till: '直到',
|
||||
unless: '除非',
|
||||
until: '直到',
|
||||
when: '当...时候',
|
||||
whenever: '每当',
|
||||
where: '哪里',
|
||||
wherever: '无论哪里',
|
||||
whereas: '然而',
|
||||
wherever: '无论哪里',
|
||||
while: '当...时候',
|
||||
why: '为什么',
|
||||
},
|
||||
|
||||
'en-US': {
|
||||
close: 'Close',
|
||||
loading: 'Loading...',
|
||||
success: 'Success',
|
||||
error: 'Error',
|
||||
warning: 'Warning',
|
||||
info: 'Info',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
ok: 'OK',
|
||||
retry: 'Retry',
|
||||
undo: 'Undo',
|
||||
redo: 'Redo',
|
||||
save: 'Save',
|
||||
delete: 'Delete',
|
||||
edit: 'Edit',
|
||||
add: 'Add',
|
||||
remove: 'Remove',
|
||||
search: 'Search',
|
||||
filter: 'Filter',
|
||||
sort: 'Sort',
|
||||
refresh: 'Refresh',
|
||||
sync: 'Sync',
|
||||
upload: 'Upload',
|
||||
download: 'Download',
|
||||
share: 'Share',
|
||||
copy: 'Copy',
|
||||
cut: 'Cut',
|
||||
paste: 'Paste',
|
||||
print: 'Print',
|
||||
export: 'Export',
|
||||
import: 'Import',
|
||||
settings: 'Settings',
|
||||
help: 'Help',
|
||||
about: 'About',
|
||||
version: 'Version',
|
||||
language: 'Language',
|
||||
theme: 'Theme',
|
||||
dark: 'Dark',
|
||||
light: 'Light',
|
||||
auto: 'Auto',
|
||||
notifications: 'Notifications',
|
||||
enable: 'Enable',
|
||||
disable: 'Disable',
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
max: 'Max',
|
||||
min: 'Min',
|
||||
expand: 'Expand',
|
||||
collapse: 'Collapse',
|
||||
fullscreen: 'Fullscreen',
|
||||
exitFullscreen: 'Exit Fullscreen',
|
||||
zoomIn: 'Zoom In',
|
||||
zoomOut: 'Zoom Out',
|
||||
reset: 'Reset',
|
||||
apply: 'Apply',
|
||||
submit: 'Submit',
|
||||
send: 'Send',
|
||||
receive: 'Receive',
|
||||
connect: 'Connect',
|
||||
disconnect: 'Disconnect',
|
||||
start: 'Start',
|
||||
stop: 'Stop',
|
||||
pause: 'Pause',
|
||||
resume: 'Resume',
|
||||
next: 'Next',
|
||||
previous: 'Previous',
|
||||
first: 'First',
|
||||
last: 'Last',
|
||||
back: 'Back',
|
||||
forward: 'Forward',
|
||||
home: 'Home',
|
||||
menu: 'Menu',
|
||||
more: 'More',
|
||||
less: 'Less',
|
||||
show: 'Show',
|
||||
hide: 'Hide',
|
||||
visible: 'Visible',
|
||||
hidden: 'Hidden',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
active: 'Active',
|
||||
inactive: 'Inactive',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
connected: 'Connected',
|
||||
disconnected: 'Disconnected',
|
||||
loading: 'Loading',
|
||||
loaded: 'Loaded',
|
||||
saving: 'Saving',
|
||||
saved: 'Saved',
|
||||
deleting: 'Deleting',
|
||||
deleted: 'Deleted',
|
||||
uploading: 'Uploading',
|
||||
uploaded: 'Uploaded',
|
||||
downloading: 'Downloading',
|
||||
downloaded: 'Downloaded',
|
||||
processing: 'Processing',
|
||||
processed: 'Processed',
|
||||
syncing: 'Syncing',
|
||||
synced: 'Synced',
|
||||
sending: 'Sending',
|
||||
sent: 'Sent',
|
||||
receiving: 'Receiving',
|
||||
received: 'Received',
|
||||
connecting: 'Connecting',
|
||||
error: 'Error',
|
||||
warning: 'Warning',
|
||||
info: 'Info',
|
||||
success: 'Success',
|
||||
question: 'Question',
|
||||
help: 'Help',
|
||||
feedback: 'Feedback',
|
||||
report: 'Report',
|
||||
bug: 'Bug',
|
||||
feature: 'Feature',
|
||||
improvement: 'Improvement',
|
||||
suggestion: 'Suggestion',
|
||||
request: 'Request',
|
||||
response: 'Response',
|
||||
status: 'Status',
|
||||
progress: 'Progress',
|
||||
complete: 'Complete',
|
||||
incomplete: 'Incomplete',
|
||||
pending: 'Pending',
|
||||
processing: 'Processing',
|
||||
failed: 'Failed',
|
||||
cancelled: 'Cancelled',
|
||||
timeout: 'Timeout',
|
||||
expired: 'Expired',
|
||||
invalid: 'Invalid',
|
||||
valid: 'Valid',
|
||||
required: 'Required',
|
||||
optional: 'Optional',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
allowed: 'Allowed',
|
||||
denied: 'Denied',
|
||||
approved: 'Approved',
|
||||
rejected: 'Rejected',
|
||||
accepted: 'Accepted',
|
||||
declined: 'Declined',
|
||||
confirmed: 'Confirmed',
|
||||
unconfirmed: 'Unconfirmed',
|
||||
verified: 'Verified',
|
||||
unverified: 'Unverified',
|
||||
authenticated: 'Authenticated',
|
||||
unauthenticated: 'Unauthenticated',
|
||||
authorized: 'Authorized',
|
||||
unauthorized: 'Unauthorized',
|
||||
public: 'Public',
|
||||
private: 'Private',
|
||||
protected: 'Protected',
|
||||
internal: 'Internal',
|
||||
external: 'External',
|
||||
local: 'Local',
|
||||
remote: 'Remote',
|
||||
global: 'Global',
|
||||
system: 'System',
|
||||
user: 'User',
|
||||
admin: 'Admin',
|
||||
moderator: 'Moderator',
|
||||
guest: 'Guest',
|
||||
member: 'Member',
|
||||
owner: 'Owner',
|
||||
creator: 'Creator',
|
||||
editor: 'Editor',
|
||||
viewer: 'Viewer',
|
||||
subscriber: 'Subscriber',
|
||||
follower: 'Follower',
|
||||
following: 'Following',
|
||||
friend: 'Friend',
|
||||
contact: 'Contact',
|
||||
group: 'Group',
|
||||
team: 'Team',
|
||||
organization: 'Organization',
|
||||
company: 'Company',
|
||||
department: 'Department',
|
||||
project: 'Project',
|
||||
task: 'Task',
|
||||
issue: 'Issue',
|
||||
ticket: 'Ticket',
|
||||
request: 'Request',
|
||||
response: 'Response',
|
||||
message: 'Message',
|
||||
notification: 'Notification',
|
||||
alert: 'Alert',
|
||||
reminder: 'Reminder',
|
||||
event: 'Event',
|
||||
activity: 'Activity',
|
||||
log: 'Log',
|
||||
report: 'Report',
|
||||
analytics: 'Analytics',
|
||||
statistics: 'Statistics',
|
||||
metrics: 'Metrics',
|
||||
performance: 'Performance',
|
||||
security: 'Security',
|
||||
privacy: 'Privacy',
|
||||
compliance: 'Compliance',
|
||||
policy: 'Policy',
|
||||
terms: 'Terms',
|
||||
conditions: 'Conditions',
|
||||
agreement: 'Agreement',
|
||||
contract: 'Contract',
|
||||
license: 'License',
|
||||
copyright: 'Copyright',
|
||||
trademark: 'Trademark',
|
||||
patent: 'Patent',
|
||||
tradeSecret: 'Trade Secret',
|
||||
confidential: 'Confidential',
|
||||
sensitive: 'Sensitive',
|
||||
critical: 'Critical',
|
||||
important: 'Important',
|
||||
urgent: 'Urgent',
|
||||
high: 'High',
|
||||
medium: 'Medium',
|
||||
low: 'Low',
|
||||
none: 'None',
|
||||
all: 'All',
|
||||
any: 'Any',
|
||||
some: 'Some',
|
||||
each: 'Each',
|
||||
every: 'Every',
|
||||
both: 'Both',
|
||||
neither: 'Neither',
|
||||
either: 'Either',
|
||||
other: 'Other',
|
||||
another: 'Another',
|
||||
this: 'This',
|
||||
that: 'That',
|
||||
these: 'These',
|
||||
those: 'Those',
|
||||
here: 'Here',
|
||||
there: 'There',
|
||||
where: 'Where',
|
||||
when: 'When',
|
||||
why: 'Why',
|
||||
how: 'How',
|
||||
what: 'What',
|
||||
who: 'Who',
|
||||
which: 'Which',
|
||||
whose: 'Whose',
|
||||
whom: 'Whom',
|
||||
myself: 'Myself',
|
||||
yourself: 'Yourself',
|
||||
himself: 'Himself',
|
||||
herself: 'Herself',
|
||||
itself: 'Itself',
|
||||
ourselves: 'Ourselves',
|
||||
yourselves: 'Yourselves',
|
||||
themselves: 'Themselves',
|
||||
mine: 'Mine',
|
||||
yours: 'Yours',
|
||||
his: 'His',
|
||||
hers: 'Hers',
|
||||
its: 'Its',
|
||||
ours: 'Ours',
|
||||
yours: 'Yours',
|
||||
theirs: 'Theirs',
|
||||
me: 'Me',
|
||||
you: 'You',
|
||||
him: 'Him',
|
||||
her: 'Her',
|
||||
it: 'It',
|
||||
us: 'Us',
|
||||
them: 'Them',
|
||||
my: 'My',
|
||||
your: 'Your',
|
||||
his: 'His',
|
||||
her: 'Her',
|
||||
its: 'Its',
|
||||
our: 'Our',
|
||||
your: 'Your',
|
||||
their: 'Their',
|
||||
i: 'I',
|
||||
you: 'You',
|
||||
he: 'He',
|
||||
she: 'She',
|
||||
it: 'It',
|
||||
we: 'We',
|
||||
they: 'They',
|
||||
am: 'Am',
|
||||
is: 'Is',
|
||||
are: 'Are',
|
||||
was: 'Was',
|
||||
were: 'Were',
|
||||
be: 'Be',
|
||||
been: 'Been',
|
||||
being: 'Being',
|
||||
have: 'Have',
|
||||
has: 'Has',
|
||||
had: 'Had',
|
||||
having: 'Having',
|
||||
do: 'Do',
|
||||
does: 'Does',
|
||||
did: 'Did',
|
||||
doing: 'Doing',
|
||||
will: 'Will',
|
||||
would: 'Would',
|
||||
shall: 'Shall',
|
||||
should: 'Should',
|
||||
may: 'May',
|
||||
might: 'Might',
|
||||
can: 'Can',
|
||||
could: 'Could',
|
||||
must: 'Must',
|
||||
need: 'Need',
|
||||
dare: 'Dare',
|
||||
ought: 'Ought',
|
||||
used: 'Used',
|
||||
to: 'To',
|
||||
of: 'Of',
|
||||
in: 'In',
|
||||
for: 'For',
|
||||
on: 'On',
|
||||
with: 'With',
|
||||
at: 'At',
|
||||
by: 'By',
|
||||
from: 'From',
|
||||
into: 'Into',
|
||||
during: 'During',
|
||||
before: 'Before',
|
||||
after: 'After',
|
||||
above: 'Above',
|
||||
below: 'Below',
|
||||
between: 'Between',
|
||||
under: 'Under',
|
||||
over: 'Over',
|
||||
across: 'Across',
|
||||
through: 'Through',
|
||||
into: 'Into',
|
||||
towards: 'Towards',
|
||||
upon: 'Upon',
|
||||
about: 'About',
|
||||
against: 'Against',
|
||||
among: 'Among',
|
||||
along: 'Along',
|
||||
around: 'Around',
|
||||
beyond: 'Beyond',
|
||||
but: 'But',
|
||||
despite: 'Despite',
|
||||
except: 'Except',
|
||||
inside: 'Inside',
|
||||
outside: 'Outside',
|
||||
since: 'Since',
|
||||
until: 'Until',
|
||||
unless: 'Unless',
|
||||
whether: 'Whether',
|
||||
while: 'While',
|
||||
although: 'Although',
|
||||
because: 'Because',
|
||||
if: 'If',
|
||||
once: 'Once',
|
||||
since: 'Since',
|
||||
so: 'So',
|
||||
that: 'That',
|
||||
though: 'Though',
|
||||
till: 'Till',
|
||||
unless: 'Unless',
|
||||
until: 'Until',
|
||||
when: 'When',
|
||||
whenever: 'Whenever',
|
||||
where: 'Where',
|
||||
wherever: 'Wherever',
|
||||
whereas: 'Whereas',
|
||||
wherever: 'Wherever',
|
||||
while: 'While',
|
||||
why: 'Why',
|
||||
},
|
||||
};
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* MetonaToast Plugins - 插件系统
|
||||
* @module plugins
|
||||
* @version 2.0.0
|
||||
* @description 插件管理器 + 3 款预设插件 (keyboard / persistence / accessibility)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 插件管理器
|
||||
*/
|
||||
class PluginManager {
|
||||
constructor() {
|
||||
this.plugins = new Map();
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
register(name, plugin) {
|
||||
if (this.plugins.has(name)) {
|
||||
console.warn(`Plugin "${name}" is already registered`);
|
||||
return this;
|
||||
}
|
||||
if (!plugin || typeof plugin !== 'object' || (!plugin.name && !plugin.version)) {
|
||||
console.error(`Invalid plugin "${name}"`);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.plugins.set(name, { name, ...plugin, installed: false, enabled: true });
|
||||
|
||||
if (plugin.install) {
|
||||
try { plugin.install(this); this.plugins.get(name).installed = true; }
|
||||
catch (e) { console.error(`Failed to install plugin "${name}":`, e); }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
unregister(name) {
|
||||
const plugin = this.plugins.get(name);
|
||||
if (!plugin) return this;
|
||||
if (plugin.uninstall) {
|
||||
try { plugin.uninstall(this); } catch (e) { console.error(`Failed to uninstall plugin "${name}":`, e); }
|
||||
}
|
||||
this.plugins.delete(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
get(name) { return this.plugins.get(name) || null; }
|
||||
has(name) { return this.plugins.has(name); }
|
||||
getAll() { return Array.from(this.plugins.values()); }
|
||||
getNames() { return Array.from(this.plugins.keys()); }
|
||||
|
||||
enable(name) {
|
||||
const p = this.plugins.get(name);
|
||||
if (p) p.enabled = true;
|
||||
return this;
|
||||
}
|
||||
disable(name) {
|
||||
const p = this.plugins.get(name);
|
||||
if (p) p.enabled = false;
|
||||
return this;
|
||||
}
|
||||
isEnabled(name) {
|
||||
const p = this.plugins.get(name);
|
||||
return p ? p.enabled : false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.plugins.forEach((p, name) => {
|
||||
if (p.destroy) { try { p.destroy(this); } catch (e) {} }
|
||||
});
|
||||
this.plugins.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预设插件
|
||||
*/
|
||||
const presetPlugins = {
|
||||
|
||||
/**
|
||||
* 键盘快捷键插件 — ESC 关闭所有 Toast
|
||||
*/
|
||||
keyboard: {
|
||||
name: 'keyboard',
|
||||
version: '1.0.0',
|
||||
description: 'ESC 关闭所有 Toast',
|
||||
|
||||
_handler: null,
|
||||
|
||||
install() {
|
||||
if (typeof document === 'undefined') return;
|
||||
this._handler = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
// 点击所有关闭按钮
|
||||
document.querySelectorAll('.met-toast .met-close').forEach(btn => {
|
||||
try { btn.click(); } catch (_) {}
|
||||
});
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', this._handler);
|
||||
},
|
||||
|
||||
uninstall() {
|
||||
if (this._handler && typeof document !== 'undefined') {
|
||||
document.removeEventListener('keydown', this._handler);
|
||||
this._handler = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 持久化插件 — 自动保存/加载配置到 localStorage
|
||||
*/
|
||||
persistence: {
|
||||
name: 'persistence',
|
||||
version: '1.0.0',
|
||||
description: '自动持久化配置到 localStorage',
|
||||
storageKey: 'metona-toast-config',
|
||||
|
||||
install() {
|
||||
// 加载已保存的配置
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
const saved = localStorage.getItem(this.storageKey);
|
||||
return saved ? JSON.parse(saved) : null;
|
||||
} catch (_) { return null; }
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
save(config) {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try { localStorage.setItem(this.storageKey, JSON.stringify(config)); } catch (_) {}
|
||||
}
|
||||
},
|
||||
|
||||
uninstall() {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try { localStorage.removeItem(this.storageKey); } catch (_) {}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 无障碍插件 — 屏幕阅读器公告
|
||||
*/
|
||||
accessibility: {
|
||||
name: 'accessibility',
|
||||
version: '1.0.0',
|
||||
description: '通过屏幕阅读器朗读 Toast 内容',
|
||||
|
||||
announce(toast) {
|
||||
if (typeof document === 'undefined') return;
|
||||
const el = document.createElement('div');
|
||||
el.setAttribute('aria-live', 'assertive');
|
||||
el.setAttribute('aria-atomic', 'true');
|
||||
el.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';
|
||||
const typeNames = { success:'成功', error:'错误', warning:'警告', info:'信息', loading:'加载中', default:'通知' };
|
||||
el.textContent = `${typeNames[toast.type] || '通知'}: ${toast.title || ''} ${toast.message || ''}`;
|
||||
document.body.appendChild(el);
|
||||
setTimeout(() => { if (el.parentNode) el.parentNode.removeChild(el); }, 3000);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 插件工具
|
||||
*/
|
||||
const pluginUtils = {
|
||||
createManager() { return new PluginManager(); },
|
||||
register(name, plugin) { return defaultPluginManager.register(name, plugin); },
|
||||
unregister(name) { return defaultPluginManager.unregister(name); },
|
||||
get(name) { return defaultPluginManager.get(name); },
|
||||
has(name) { return defaultPluginManager.has(name); },
|
||||
getAll() { return defaultPluginManager.getAll(); },
|
||||
getNames() { return defaultPluginManager.getNames(); },
|
||||
enable(name) { return defaultPluginManager.enable(name); },
|
||||
disable(name) { return defaultPluginManager.disable(name); },
|
||||
isEnabled(name) { return defaultPluginManager.isEnabled(name); },
|
||||
getPreset(name) { return presetPlugins[name] || null; },
|
||||
getAllPresets() { return { ...presetPlugins }; },
|
||||
|
||||
createPlugin(config) {
|
||||
return {
|
||||
name: config.name || 'custom',
|
||||
version: config.version || '1.0.0',
|
||||
description: config.description || '',
|
||||
hooks: config.hooks || {},
|
||||
install: config.install || null,
|
||||
uninstall: config.uninstall || null,
|
||||
...config,
|
||||
};
|
||||
},
|
||||
|
||||
validatePlugin(plugin) {
|
||||
const errors = [];
|
||||
if (!plugin || typeof plugin !== 'object') errors.push('Plugin must be an object');
|
||||
if (!plugin.name && !plugin.version) errors.push('Plugin must have a name or version');
|
||||
return { valid: errors.length === 0, errors };
|
||||
},
|
||||
};
|
||||
|
||||
const defaultPluginManager = new PluginManager();
|
||||
|
||||
export { presetPlugins, pluginUtils, defaultPluginManager, PluginManager };
|
||||
+1848
File diff suppressed because it is too large
Load Diff
+722
@@ -0,0 +1,722 @@
|
||||
/**
|
||||
* MetonaToast Themes - 主题管理
|
||||
* @module themes
|
||||
* @version 2.0.0
|
||||
* @description 主题系统、自定义主题和主题切换
|
||||
*/
|
||||
|
||||
import { THEMES } from './constants.js';
|
||||
import { prefersDark } from './utils.js';
|
||||
|
||||
// 当前主题状态
|
||||
let currentTheme = 'auto';
|
||||
let themeListeners = new Set();
|
||||
let systemThemeMediaQuery = null;
|
||||
let systemThemeListener = null;
|
||||
|
||||
/**
|
||||
* 获取系统主题
|
||||
* @returns {string} 系统主题 ('light' 或 'dark')
|
||||
*/
|
||||
export const getSystemTheme = () => {
|
||||
if (typeof window === 'undefined') return 'light';
|
||||
return prefersDark() ? 'dark' : 'light';
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题(别名)
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
export const getTheme = (theme) => {
|
||||
return resolveTheme(theme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析主题
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
export const resolveTheme = (theme) => {
|
||||
if (theme === 'auto') {
|
||||
// 优先使用用户设置的全局主题,否则使用系统主题
|
||||
if (currentTheme && currentTheme !== 'auto') {
|
||||
return currentTheme;
|
||||
}
|
||||
return getSystemTheme();
|
||||
}
|
||||
return theme;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题配置
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题配置
|
||||
*/
|
||||
export const getThemeConfig = (theme) => {
|
||||
const resolved = resolveTheme(theme);
|
||||
return THEMES[resolved] || THEMES.light;
|
||||
};
|
||||
|
||||
/**
|
||||
* 应用主题
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
export const applyTheme = (theme) => {
|
||||
currentTheme = theme;
|
||||
const resolved = resolveTheme(theme);
|
||||
|
||||
// 应用主题到文档
|
||||
if (typeof document !== 'undefined') {
|
||||
document.documentElement.setAttribute('data-met-theme', resolved);
|
||||
document.documentElement.classList.remove('met-theme-light', 'met-theme-dark', 'met-theme-auto');
|
||||
document.documentElement.classList.add(`met-theme-${resolved}`);
|
||||
|
||||
// 设置CSS变量
|
||||
const config = getThemeConfig(theme);
|
||||
setThemeVariables(config);
|
||||
}
|
||||
|
||||
// 通知监听器
|
||||
notifyThemeListeners(theme, resolved);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置主题CSS变量
|
||||
* @param {Object} config - 主题配置
|
||||
*/
|
||||
export const setThemeVariables = (config) => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--met-bg', config.bg);
|
||||
root.style.setProperty('--met-text', config.text);
|
||||
root.style.setProperty('--met-border', config.border);
|
||||
root.style.setProperty('--met-shadow', config.shadow);
|
||||
root.style.setProperty('--met-hover-shadow', config.hoverShadow);
|
||||
root.style.setProperty('--met-progress-bg', config.progressBg);
|
||||
root.style.setProperty('--met-close-hover-bg', config.closeHoverBg);
|
||||
|
||||
// 设置颜色变量
|
||||
root.style.setProperty('--met-success', '#10b981');
|
||||
root.style.setProperty('--met-error', '#ef4444');
|
||||
root.style.setProperty('--met-warning', '#f59e0b');
|
||||
root.style.setProperty('--met-info', '#3b82f6');
|
||||
root.style.setProperty('--met-loading', '#6366f1');
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除主题CSS变量
|
||||
*/
|
||||
export const clearThemeVariables = () => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const root = document.documentElement;
|
||||
const variables = [
|
||||
'--met-bg',
|
||||
'--met-text',
|
||||
'--met-border',
|
||||
'--met-shadow',
|
||||
'--met-hover-shadow',
|
||||
'--met-progress-bg',
|
||||
'--met-close-hover-bg',
|
||||
'--met-success',
|
||||
'--met-error',
|
||||
'--met-warning',
|
||||
'--met-info',
|
||||
'--met-loading',
|
||||
];
|
||||
|
||||
variables.forEach((variable) => {
|
||||
root.style.removeProperty(variable);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前主题
|
||||
* @returns {string} 当前主题
|
||||
*/
|
||||
export const getCurrentTheme = () => {
|
||||
return currentTheme;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取解析后的主题
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
export const getResolvedTheme = () => {
|
||||
return resolveTheme(currentTheme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @param {string} theme - 新主题
|
||||
*/
|
||||
export const switchTheme = (theme) => {
|
||||
applyTheme(theme);
|
||||
saveTheme(theme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 切换亮色/暗色主题
|
||||
*/
|
||||
export const toggleTheme = () => {
|
||||
const resolved = getResolvedTheme();
|
||||
const newTheme = resolved === 'dark' ? 'light' : 'dark';
|
||||
switchTheme(newTheme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置为自动主题
|
||||
*/
|
||||
export const resetToAuto = () => {
|
||||
switchTheme('auto');
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存主题到本地存储
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
export const saveTheme = (theme) => {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
localStorage.setItem('metona-toast-theme', theme);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save theme:', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 从本地存储加载主题
|
||||
* @returns {string} 主题名称
|
||||
*/
|
||||
export const loadTheme = () => {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
return localStorage.getItem('metona-toast-theme') || 'auto';
|
||||
} catch (e) {
|
||||
console.warn('Failed to load theme:', e);
|
||||
}
|
||||
}
|
||||
return 'auto';
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化主题系统
|
||||
*/
|
||||
export const initTheme = () => {
|
||||
// 加载保存的主题
|
||||
const savedTheme = loadTheme();
|
||||
applyTheme(savedTheme);
|
||||
|
||||
// 监听系统主题变化
|
||||
watchSystemTheme();
|
||||
};
|
||||
|
||||
/**
|
||||
* 监听系统主题变化
|
||||
*/
|
||||
export const watchSystemTheme = () => {
|
||||
if (typeof window === 'undefined' || !window.matchMedia) return;
|
||||
|
||||
// 移除旧的监听器
|
||||
if (systemThemeMediaQuery && systemThemeListener) {
|
||||
systemThemeMediaQuery.removeEventListener('change', systemThemeListener);
|
||||
}
|
||||
|
||||
// 创建新的监听器
|
||||
systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
systemThemeListener = (e) => {
|
||||
if (currentTheme === 'auto') {
|
||||
applyTheme('auto');
|
||||
}
|
||||
};
|
||||
|
||||
systemThemeMediaQuery.addEventListener('change', systemThemeListener);
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止监听系统主题变化
|
||||
*/
|
||||
export const unwatchSystemTheme = () => {
|
||||
if (systemThemeMediaQuery && systemThemeListener) {
|
||||
systemThemeMediaQuery.removeEventListener('change', systemThemeListener);
|
||||
systemThemeMediaQuery = null;
|
||||
systemThemeListener = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
* @returns {Function} 移除监听器函数
|
||||
*/
|
||||
export const addThemeListener = (listener) => {
|
||||
themeListeners.add(listener);
|
||||
|
||||
return () => {
|
||||
themeListeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
*/
|
||||
export const removeThemeListener = (listener) => {
|
||||
themeListeners.delete(listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* 通知主题监听器
|
||||
* @param {string} theme - 主题名称
|
||||
* @param {string} resolved - 解析后的主题
|
||||
*/
|
||||
const notifyThemeListeners = (theme, resolved) => {
|
||||
themeListeners.forEach((listener) => {
|
||||
try {
|
||||
listener(theme, resolved);
|
||||
} catch (e) {
|
||||
console.error('Theme listener error:', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除所有主题监听器
|
||||
*/
|
||||
export const clearThemeListeners = () => {
|
||||
themeListeners.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
* @param {Object} config - 主题配置
|
||||
*/
|
||||
export const registerTheme = (name, config) => {
|
||||
THEMES[name] = {
|
||||
bg: config.bg || THEMES.light.bg,
|
||||
text: config.text || THEMES.light.text,
|
||||
border: config.border || THEMES.light.border,
|
||||
shadow: config.shadow || THEMES.light.shadow,
|
||||
hoverShadow: config.hoverShadow || THEMES.light.hoverShadow,
|
||||
progressBg: config.progressBg || THEMES.light.progressBg,
|
||||
closeHoverBg: config.closeHoverBg || THEMES.light.closeHoverBg,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 注销自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
*/
|
||||
export const unregisterTheme = (name) => {
|
||||
if (name === 'light' || name === 'dark' || name === 'auto') {
|
||||
console.warn('Cannot unregister built-in theme:', name);
|
||||
return;
|
||||
}
|
||||
|
||||
delete THEMES[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有主题
|
||||
* @returns {Object} 主题映射
|
||||
*/
|
||||
export const getAllThemes = () => {
|
||||
return { ...THEMES };
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题名称列表
|
||||
* @returns {Array} 主题名称数组
|
||||
*/
|
||||
export const getThemeNames = () => {
|
||||
return Object.keys(THEMES);
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查主题是否存在
|
||||
* @param {string} name - 主题名称
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
export const hasTheme = (name) => {
|
||||
return name in THEMES;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题预览
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题预览
|
||||
*/
|
||||
export const getThemePreview = (theme) => {
|
||||
const config = getThemeConfig(theme);
|
||||
const resolved = resolveTheme(theme);
|
||||
|
||||
return {
|
||||
name: theme,
|
||||
resolved,
|
||||
colors: {
|
||||
background: config.bg,
|
||||
text: config.text,
|
||||
border: config.border,
|
||||
},
|
||||
isDark: resolved === 'dark',
|
||||
isLight: resolved === 'light',
|
||||
isAuto: theme === 'auto',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} CSS字符串
|
||||
*/
|
||||
export const generateThemeCSS = (theme) => {
|
||||
const config = getThemeConfig(theme);
|
||||
const resolved = resolveTheme(theme);
|
||||
|
||||
return `
|
||||
.met-theme-${resolved} {
|
||||
--met-bg: ${config.bg};
|
||||
--met-text: ${config.text};
|
||||
--met-border: ${config.border};
|
||||
--met-shadow: ${config.shadow};
|
||||
--met-hover-shadow: ${config.hoverShadow};
|
||||
--met-progress-bg: ${config.progressBg};
|
||||
--met-close-hover-bg: ${config.closeHoverBg};
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 应用主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
export const applyThemeCSS = (theme) => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const css = generateThemeCSS(theme);
|
||||
const styleId = 'metona-toast-theme-css';
|
||||
|
||||
// 移除旧样式
|
||||
const oldStyle = document.getElementById(styleId);
|
||||
if (oldStyle) {
|
||||
oldStyle.remove();
|
||||
}
|
||||
|
||||
// 添加新样式
|
||||
const style = document.createElement('style');
|
||||
style.id = styleId;
|
||||
style.textContent = css;
|
||||
document.head.appendChild(style);
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除主题CSS
|
||||
*/
|
||||
export const removeThemeCSS = () => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const styleId = 'metona-toast-theme-css';
|
||||
const style = document.getElementById(styleId);
|
||||
if (style) {
|
||||
style.remove();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 主题工具
|
||||
*/
|
||||
export const themeUtils = {
|
||||
/**
|
||||
* 获取系统主题
|
||||
* @returns {string} 系统主题
|
||||
*/
|
||||
getSystemTheme() {
|
||||
return getSystemTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析主题
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
resolveTheme(theme) {
|
||||
return resolveTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题配置
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题配置
|
||||
*/
|
||||
getThemeConfig(theme) {
|
||||
return getThemeConfig(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用主题
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
applyTheme(theme) {
|
||||
applyTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前主题
|
||||
* @returns {string} 当前主题
|
||||
*/
|
||||
getCurrentTheme() {
|
||||
return getCurrentTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取解析后的主题
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
getResolvedTheme() {
|
||||
return getResolvedTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @param {string} theme - 新主题
|
||||
*/
|
||||
switchTheme(theme) {
|
||||
switchTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换亮色/暗色主题
|
||||
*/
|
||||
toggleTheme() {
|
||||
toggleTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置为自动主题
|
||||
*/
|
||||
resetToAuto() {
|
||||
resetToAuto();
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化主题系统
|
||||
*/
|
||||
initTheme() {
|
||||
initTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听系统主题变化
|
||||
*/
|
||||
watchSystemTheme() {
|
||||
watchSystemTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止监听系统主题变化
|
||||
*/
|
||||
unwatchSystemTheme() {
|
||||
unwatchSystemTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
* @returns {Function} 移除监听器函数
|
||||
*/
|
||||
addThemeListener(listener) {
|
||||
return addThemeListener(listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
*/
|
||||
removeThemeListener(listener) {
|
||||
removeThemeListener(listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除所有主题监听器
|
||||
*/
|
||||
clearThemeListeners() {
|
||||
clearThemeListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* 注册自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
* @param {Object} config - 主题配置
|
||||
*/
|
||||
registerTheme(name, config) {
|
||||
registerTheme(name, config);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注销自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
*/
|
||||
unregisterTheme(name) {
|
||||
unregisterTheme(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有主题
|
||||
* @returns {Object} 主题映射
|
||||
*/
|
||||
getAllThemes() {
|
||||
return getAllThemes();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题名称列表
|
||||
* @returns {Array} 主题名称数组
|
||||
*/
|
||||
getThemeNames() {
|
||||
return getThemeNames();
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查主题是否存在
|
||||
* @param {string} name - 主题名称
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
hasTheme(name) {
|
||||
return hasTheme(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题预览
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题预览
|
||||
*/
|
||||
getThemePreview(theme) {
|
||||
return getThemePreview(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} CSS字符串
|
||||
*/
|
||||
generateThemeCSS(theme) {
|
||||
return generateThemeCSS(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
applyThemeCSS(theme) {
|
||||
applyThemeCSS(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除主题CSS
|
||||
*/
|
||||
removeThemeCSS() {
|
||||
removeThemeCSS();
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存主题
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
saveTheme(theme) {
|
||||
saveTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载主题
|
||||
* @returns {string} 主题名称
|
||||
*/
|
||||
loadTheme() {
|
||||
return loadTheme();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 预设主题
|
||||
*/
|
||||
export const presetThemes = {
|
||||
/**
|
||||
* 浅色主题
|
||||
*/
|
||||
light: {
|
||||
name: '浅色',
|
||||
description: '明亮清晰的主题',
|
||||
config: THEMES.light,
|
||||
},
|
||||
|
||||
/**
|
||||
* 深色主题
|
||||
*/
|
||||
dark: {
|
||||
name: '深色',
|
||||
description: '护眼舒适的暗色主题',
|
||||
config: THEMES.dark,
|
||||
},
|
||||
|
||||
/**
|
||||
* 自动主题
|
||||
*/
|
||||
auto: {
|
||||
name: '自动',
|
||||
description: '跟随系统主题设置',
|
||||
config: 'auto',
|
||||
},
|
||||
|
||||
warm: {
|
||||
name: '暖色',
|
||||
description: '温馨舒适的暖色主题',
|
||||
config: THEMES.warm,
|
||||
},
|
||||
};
|
||||
|
||||
// 注册预设主题
|
||||
Object.entries(presetThemes).forEach(([name, theme]) => {
|
||||
if (name !== 'auto' && theme.config !== 'auto') {
|
||||
THEMES[name] = theme.config;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建主题管理器
|
||||
* @returns {Object} 主题管理器
|
||||
*/
|
||||
export const createThemeManager = () => {
|
||||
return {
|
||||
getSystemTheme,
|
||||
resolveTheme,
|
||||
getThemeConfig,
|
||||
applyTheme,
|
||||
getCurrentTheme,
|
||||
getResolvedTheme,
|
||||
switchTheme,
|
||||
toggleTheme,
|
||||
resetToAuto,
|
||||
initTheme,
|
||||
watchSystemTheme,
|
||||
unwatchSystemTheme,
|
||||
addThemeListener,
|
||||
removeThemeListener,
|
||||
clearThemeListeners,
|
||||
registerTheme,
|
||||
unregisterTheme,
|
||||
getAllThemes,
|
||||
getThemeNames,
|
||||
hasTheme,
|
||||
getThemePreview,
|
||||
generateThemeCSS,
|
||||
applyThemeCSS,
|
||||
removeThemeCSS,
|
||||
saveTheme,
|
||||
loadTheme,
|
||||
};
|
||||
};
|
||||
|
||||
export { themeUtils as default };
|
||||
+1062
File diff suppressed because it is too large
Load Diff
+1933
File diff suppressed because it is too large
Load Diff
Vendored
+1037
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user