release: v0.5.0 — 覆盖率96.42%达标、lint零警告、覆盖率门禁、CI强化、发布自动化

- 覆盖率 96.42%(行):新增 59 测试共 385 个,覆盖 DOM 交互事件、对话框按钮交互、Intl/localStorage 异常路径、React 渲染生命周期
- 测试环境重构:SSR 分支用 defineProperty 真正覆盖 document/window;新增 tests/dom.test.ts、tests/setup.ts
- jest 覆盖率门禁 lines >= 95%;CI lint 改必过;新增 publish.yml(v* tag 自动发布到 Gitea registry)
- 修复:Toast.off 空数组残留、Toast.on 取消函数 this 绑定、action close:false 冒泡关闭、use() 重复注册钩子、i18n catch 引用错误
- 删除死代码 autoApplySystemTheme;lint 零警告(no-console 策略 + caughtErrorsIgnorePattern)
This commit is contained in:
tianhao
2026-08-08 15:02:51 +08:00
parent bbff631f4f
commit 1f718c4ef8
41 changed files with 1146 additions and 281 deletions
+71 -36
View File
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
/**
* MetonaToast Utils — 工具函数
* @module utils
* @version 0.4.0
* @version 0.5.0
*/
/**
* 生成唯一ID
@@ -42,7 +42,7 @@ const prefersDark = () => {
/**
* MetonaToast Icons — 图标SVG定义
* @module icons
* @version 0.4.0
* @version 0.5.0
* @description 107 个内置 SVG 图标
*/
const ICONS = {
@@ -539,7 +539,7 @@ const ICONS = {
/**
* MetonaToast Locales — 国际化翻译数据
* @module locales
* @version 0.4.0
* @version 0.5.0
* @description 内置 zh-CN / en-US 完整翻译
*/
const LOCALES = {
@@ -1026,12 +1026,12 @@ const LOCALES = {
/**
* MetonaToast Constants — 常量定义
* @module constants
* @version 0.4.0
* @version 0.5.0
*/
/**
* 版本号 — 唯一来源,发布时只需修改此处
*/
const VERSION = '0.4.0';
const VERSION = '0.5.0';
/**
* 默认配置
*/
@@ -1298,7 +1298,7 @@ const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', '
/**
* MetonaToast Styles — 样式管理
* @module styles
* @version 0.4.0
* @version 0.5.0
*/
// 样式缓存
let styleElement = null;
@@ -1787,7 +1787,7 @@ const injectStyles = () => {
/**
* MetonaToast Themes — 主题管理
* @module themes
* @version 0.4.0
* @version 0.5.0
*/
// 当前主题状态
let currentTheme = 'auto';
@@ -2131,7 +2131,7 @@ const themeUtils = {
/**
* MetonaToast i18n — 国际化管理
* @module i18n
* @version 0.4.0
* @version 0.5.0
*/
// 当前语言状态
let currentLocale = 'zh-CN';
@@ -2499,7 +2499,7 @@ const formatNumber = (number, options = {}) => {
try {
return new Intl.NumberFormat(currentLocale, options).format(number);
}
catch (e) {
catch {
return number.toString();
}
};
@@ -2514,7 +2514,7 @@ const formatCurrency = (amount, currency = 'USD', options = {}) => {
...options,
}).format(amount);
}
catch (e) {
catch {
return amount.toString();
}
};
@@ -2530,7 +2530,7 @@ const formatPercent = (value, options = {}) => {
...options,
}).format(value / 100);
}
catch (e) {
catch {
return `${value}%`;
}
};
@@ -2542,7 +2542,7 @@ const formatDate = (date, options = {}) => {
const dateObj = date instanceof Date ? date : new Date(date);
return new Intl.DateTimeFormat(currentLocale, options).format(dateObj);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2586,7 +2586,7 @@ const formatRelativeTime = (date, options = {}) => {
}
return String(date);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2613,7 +2613,7 @@ const formatPlural = (count, options = {}) => {
try {
return new Intl.PluralRules(currentLocale, options).select(count);
}
catch (e) {
catch {
return count === 1 ? 'one' : 'other';
}
};
@@ -2806,7 +2806,7 @@ const animationUtils = {
/**
* MetonaToast Toast — Toast 类
* @module toast
* @version 0.4.0
* @version 0.5.0
*/
/**
* Toast 类 — 核心通知组件
@@ -2825,12 +2825,19 @@ class Toast {
if (!this._hooks.has(name))
this._hooks.set(name, []);
this._hooks.get(name).push(fn);
return () => this.off(name, fn);
// 用类名引用而非 this,保证返回的取消函数在任意调用上下文可用
return () => Toast.off(name, fn);
}
static off(name, fn) {
const list = this._hooks.get(name);
if (list)
this._hooks.set(name, list.filter(f => f !== fn));
if (list) {
const next = list.filter(f => f !== fn);
// 空数组直接删除 key,避免残留
if (next.length === 0)
this._hooks.delete(name);
else
this._hooks.set(name, next);
}
}
/**
* 触发钩子 — 如果任意钩子返回 false 则整体返回 false
@@ -3438,7 +3445,7 @@ const _containerCache = new Map();
/**
* MetonaToast Templates — HTML 模板辅助函数
* @module templates
* @version 0.4.0
* @version 0.5.0
* @description confirm / prompt / progress / action 的 DOM 模板
*/
/**
@@ -3510,7 +3517,7 @@ const actionHTML = (actions) => {
/**
* MetonaToast Plugins — 插件系统
* @module plugins
* @version 0.4.0
* @version 0.5.0
*/
/**
* 插件管理器
@@ -3665,6 +3672,12 @@ const presetPlugins = {
}
catch (_e) { /* noop */ }
}
// 卸载 use() 注册的配置保存钩子
const meta = this;
if (typeof meta._saveOff === 'function') {
meta._saveOff();
meta._saveOff = null;
}
},
},
/**
@@ -3687,6 +3700,14 @@ const presetPlugins = {
setTimeout(() => { if (el.parentNode)
el.parentNode.removeChild(el); }, 3000);
},
uninstall() {
// 卸载 use() 注册的朗读钩子
const meta = this;
if (typeof meta._off === 'function') {
meta._off();
meta._off = null;
}
},
},
/**
* 去重插件 — 相同 type + message 的 toast 自动合并(更新已有的一条)
@@ -3756,7 +3777,7 @@ const defaultPluginManager = new PluginManager();
/**
* MetonaToast API — meToast 核心 API 对象
* @module api
* @version 0.4.0
* @version 0.5.0
*/
/**
* 参数标准化工具
@@ -4092,12 +4113,14 @@ const meToast = {
actions.forEach((a, i) => {
const btn = toast.el?.querySelector(`.met-action-btn-${i}`);
if (btn && typeof a.onClick === 'function') {
btn.addEventListener('click', () => {
btn.addEventListener('click', (e) => {
// 阻止冒泡到 toast 的 closeOnClick 处理器,由 close 选项控制是否关闭
e.stopPropagation();
try {
a.onClick(toast);
}
catch (e) {
console.error('Action onClick error:', e);
catch (err) {
console.error('Action onClick error:', err);
}
if (a.close !== false)
toast.close();
@@ -4334,25 +4357,37 @@ const meToast = {
return this;
}
this.plugins.register(plugin, { ...preset, ...options });
// 连接插件钩子
// 连接插件钩子(重复 use 前先卸载旧钩子,防止重复注册)
// 注意:off 引用存在已注册的插件对象上(stored),uninstall 时 this 即该对象
const stored = this.plugins.get(plugin);
if (plugin === 'accessibility') {
Toast.on('afterShow', (toast) => {
const acc = preset;
if (typeof acc.announce === 'function')
acc.announce(toast);
});
if (stored && typeof stored._off === 'function') {
stored._off();
}
const acc = preset;
if (stored) {
stored._off = Toast.on('afterShow', (toast) => {
if (typeof acc.announce === 'function')
acc.announce(toast);
});
}
}
if (plugin === 'persistence') {
if (stored && typeof stored._saveOff === 'function') {
stored._saveOff();
}
const saved = typeof preset.install === 'function'
? preset.install(this.plugins)
: null;
if (saved)
this.configure(saved);
Toast.on('afterClose', () => {
const p = preset;
if (typeof p.save === 'function')
p.save(this.getConfig());
});
const p = preset;
if (stored) {
stored._saveOff = Toast.on('afterClose', () => {
if (typeof p.save === 'function')
p.save(this.getConfig());
});
}
}
}
else if (plugin && typeof plugin === 'object') {
@@ -4428,7 +4463,7 @@ Toast.setCallbacks({
/**
* MetonaToast — 轻量级Toast通知库
* @module metona-toast
* @version 0.4.0
* @version 0.5.0
* @author thzxx
* @license MIT
*/
+1 -1
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -1,7 +1,7 @@
/**
* MetonaToast — 核心类型定义
* @module types
* @version 0.4.0
* @version 0.5.0
*/
/** Toast 通知类型(107种内置图标类型) */
type ToastType = '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';
@@ -418,7 +418,7 @@ declare global {
/**
* MetonaToast Toast — Toast 类
* @module toast
* @version 0.4.0
* @version 0.5.0
*/
/**
@@ -492,13 +492,13 @@ declare class Toast implements ToastInstance {
/**
* MetonaToast Constants — 常量定义
* @module constants
* @version 0.4.0
* @version 0.5.0
*/
/**
* 版本号 — 唯一来源,发布时只需修改此处
*/
declare const VERSION = "0.4.0";
declare const VERSION = "0.5.0";
/**
* 增强的 MeToast 对象 — 在 meToast 基础上注入子模块
+71 -36
View File
@@ -1,7 +1,7 @@
/**
* MetonaToast Utils — 工具函数
* @module utils
* @version 0.4.0
* @version 0.5.0
*/
/**
* 生成唯一ID
@@ -38,7 +38,7 @@ const prefersDark = () => {
/**
* MetonaToast Icons — 图标SVG定义
* @module icons
* @version 0.4.0
* @version 0.5.0
* @description 107 个内置 SVG 图标
*/
const ICONS = {
@@ -535,7 +535,7 @@ const ICONS = {
/**
* MetonaToast Locales — 国际化翻译数据
* @module locales
* @version 0.4.0
* @version 0.5.0
* @description 内置 zh-CN / en-US 完整翻译
*/
const LOCALES = {
@@ -1022,12 +1022,12 @@ const LOCALES = {
/**
* MetonaToast Constants — 常量定义
* @module constants
* @version 0.4.0
* @version 0.5.0
*/
/**
* 版本号 — 唯一来源,发布时只需修改此处
*/
const VERSION = '0.4.0';
const VERSION = '0.5.0';
/**
* 默认配置
*/
@@ -1294,7 +1294,7 @@ const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', '
/**
* MetonaToast Styles — 样式管理
* @module styles
* @version 0.4.0
* @version 0.5.0
*/
// 样式缓存
let styleElement = null;
@@ -1783,7 +1783,7 @@ const injectStyles = () => {
/**
* MetonaToast Themes — 主题管理
* @module themes
* @version 0.4.0
* @version 0.5.0
*/
// 当前主题状态
let currentTheme = 'auto';
@@ -2127,7 +2127,7 @@ const themeUtils = {
/**
* MetonaToast i18n — 国际化管理
* @module i18n
* @version 0.4.0
* @version 0.5.0
*/
// 当前语言状态
let currentLocale = 'zh-CN';
@@ -2495,7 +2495,7 @@ const formatNumber = (number, options = {}) => {
try {
return new Intl.NumberFormat(currentLocale, options).format(number);
}
catch (e) {
catch {
return number.toString();
}
};
@@ -2510,7 +2510,7 @@ const formatCurrency = (amount, currency = 'USD', options = {}) => {
...options,
}).format(amount);
}
catch (e) {
catch {
return amount.toString();
}
};
@@ -2526,7 +2526,7 @@ const formatPercent = (value, options = {}) => {
...options,
}).format(value / 100);
}
catch (e) {
catch {
return `${value}%`;
}
};
@@ -2538,7 +2538,7 @@ const formatDate = (date, options = {}) => {
const dateObj = date instanceof Date ? date : new Date(date);
return new Intl.DateTimeFormat(currentLocale, options).format(dateObj);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2582,7 +2582,7 @@ const formatRelativeTime = (date, options = {}) => {
}
return String(date);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2609,7 +2609,7 @@ const formatPlural = (count, options = {}) => {
try {
return new Intl.PluralRules(currentLocale, options).select(count);
}
catch (e) {
catch {
return count === 1 ? 'one' : 'other';
}
};
@@ -2802,7 +2802,7 @@ const animationUtils = {
/**
* MetonaToast Toast — Toast 类
* @module toast
* @version 0.4.0
* @version 0.5.0
*/
/**
* Toast 类 — 核心通知组件
@@ -2821,12 +2821,19 @@ class Toast {
if (!this._hooks.has(name))
this._hooks.set(name, []);
this._hooks.get(name).push(fn);
return () => this.off(name, fn);
// 用类名引用而非 this,保证返回的取消函数在任意调用上下文可用
return () => Toast.off(name, fn);
}
static off(name, fn) {
const list = this._hooks.get(name);
if (list)
this._hooks.set(name, list.filter(f => f !== fn));
if (list) {
const next = list.filter(f => f !== fn);
// 空数组直接删除 key,避免残留
if (next.length === 0)
this._hooks.delete(name);
else
this._hooks.set(name, next);
}
}
/**
* 触发钩子 — 如果任意钩子返回 false 则整体返回 false
@@ -3434,7 +3441,7 @@ const _containerCache = new Map();
/**
* MetonaToast Templates — HTML 模板辅助函数
* @module templates
* @version 0.4.0
* @version 0.5.0
* @description confirm / prompt / progress / action 的 DOM 模板
*/
/**
@@ -3506,7 +3513,7 @@ const actionHTML = (actions) => {
/**
* MetonaToast Plugins — 插件系统
* @module plugins
* @version 0.4.0
* @version 0.5.0
*/
/**
* 插件管理器
@@ -3661,6 +3668,12 @@ const presetPlugins = {
}
catch (_e) { /* noop */ }
}
// 卸载 use() 注册的配置保存钩子
const meta = this;
if (typeof meta._saveOff === 'function') {
meta._saveOff();
meta._saveOff = null;
}
},
},
/**
@@ -3683,6 +3696,14 @@ const presetPlugins = {
setTimeout(() => { if (el.parentNode)
el.parentNode.removeChild(el); }, 3000);
},
uninstall() {
// 卸载 use() 注册的朗读钩子
const meta = this;
if (typeof meta._off === 'function') {
meta._off();
meta._off = null;
}
},
},
/**
* 去重插件 — 相同 type + message 的 toast 自动合并(更新已有的一条)
@@ -3752,7 +3773,7 @@ const defaultPluginManager = new PluginManager();
/**
* MetonaToast API — meToast 核心 API 对象
* @module api
* @version 0.4.0
* @version 0.5.0
*/
/**
* 参数标准化工具
@@ -4088,12 +4109,14 @@ const meToast = {
actions.forEach((a, i) => {
const btn = toast.el?.querySelector(`.met-action-btn-${i}`);
if (btn && typeof a.onClick === 'function') {
btn.addEventListener('click', () => {
btn.addEventListener('click', (e) => {
// 阻止冒泡到 toast 的 closeOnClick 处理器,由 close 选项控制是否关闭
e.stopPropagation();
try {
a.onClick(toast);
}
catch (e) {
console.error('Action onClick error:', e);
catch (err) {
console.error('Action onClick error:', err);
}
if (a.close !== false)
toast.close();
@@ -4330,25 +4353,37 @@ const meToast = {
return this;
}
this.plugins.register(plugin, { ...preset, ...options });
// 连接插件钩子
// 连接插件钩子(重复 use 前先卸载旧钩子,防止重复注册)
// 注意:off 引用存在已注册的插件对象上(stored),uninstall 时 this 即该对象
const stored = this.plugins.get(plugin);
if (plugin === 'accessibility') {
Toast.on('afterShow', (toast) => {
const acc = preset;
if (typeof acc.announce === 'function')
acc.announce(toast);
});
if (stored && typeof stored._off === 'function') {
stored._off();
}
const acc = preset;
if (stored) {
stored._off = Toast.on('afterShow', (toast) => {
if (typeof acc.announce === 'function')
acc.announce(toast);
});
}
}
if (plugin === 'persistence') {
if (stored && typeof stored._saveOff === 'function') {
stored._saveOff();
}
const saved = typeof preset.install === 'function'
? preset.install(this.plugins)
: null;
if (saved)
this.configure(saved);
Toast.on('afterClose', () => {
const p = preset;
if (typeof p.save === 'function')
p.save(this.getConfig());
});
const p = preset;
if (stored) {
stored._saveOff = Toast.on('afterClose', () => {
if (typeof p.save === 'function')
p.save(this.getConfig());
});
}
}
}
else if (plugin && typeof plugin === 'object') {
@@ -4424,7 +4459,7 @@ Toast.setCallbacks({
/**
* MetonaToast — 轻量级Toast通知库
* @module metona-toast
* @version 0.4.0
* @version 0.5.0
* @author thzxx
* @license MIT
*/
+1 -1
View File
File diff suppressed because one or more lines are too long
+71 -36
View File
@@ -7,7 +7,7 @@
/**
* MetonaToast Utils — 工具函数
* @module utils
* @version 0.4.0
* @version 0.5.0
*/
/**
* 生成唯一ID
@@ -44,7 +44,7 @@
/**
* MetonaToast Icons — 图标SVG定义
* @module icons
* @version 0.4.0
* @version 0.5.0
* @description 107 个内置 SVG 图标
*/
const ICONS = {
@@ -541,7 +541,7 @@
/**
* MetonaToast Locales — 国际化翻译数据
* @module locales
* @version 0.4.0
* @version 0.5.0
* @description 内置 zh-CN / en-US 完整翻译
*/
const LOCALES = {
@@ -1028,12 +1028,12 @@
/**
* MetonaToast Constants — 常量定义
* @module constants
* @version 0.4.0
* @version 0.5.0
*/
/**
* 版本号 — 唯一来源,发布时只需修改此处
*/
const VERSION = '0.4.0';
const VERSION = '0.5.0';
/**
* 默认配置
*/
@@ -1300,7 +1300,7 @@
/**
* MetonaToast Styles — 样式管理
* @module styles
* @version 0.4.0
* @version 0.5.0
*/
// 样式缓存
let styleElement = null;
@@ -1789,7 +1789,7 @@
/**
* MetonaToast Themes — 主题管理
* @module themes
* @version 0.4.0
* @version 0.5.0
*/
// 当前主题状态
let currentTheme = 'auto';
@@ -2133,7 +2133,7 @@
/**
* MetonaToast i18n — 国际化管理
* @module i18n
* @version 0.4.0
* @version 0.5.0
*/
// 当前语言状态
let currentLocale = 'zh-CN';
@@ -2501,7 +2501,7 @@
try {
return new Intl.NumberFormat(currentLocale, options).format(number);
}
catch (e) {
catch {
return number.toString();
}
};
@@ -2516,7 +2516,7 @@
...options,
}).format(amount);
}
catch (e) {
catch {
return amount.toString();
}
};
@@ -2532,7 +2532,7 @@
...options,
}).format(value / 100);
}
catch (e) {
catch {
return `${value}%`;
}
};
@@ -2544,7 +2544,7 @@
const dateObj = date instanceof Date ? date : new Date(date);
return new Intl.DateTimeFormat(currentLocale, options).format(dateObj);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2588,7 +2588,7 @@
}
return String(date);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2615,7 +2615,7 @@
try {
return new Intl.PluralRules(currentLocale, options).select(count);
}
catch (e) {
catch {
return count === 1 ? 'one' : 'other';
}
};
@@ -2808,7 +2808,7 @@
/**
* MetonaToast Toast — Toast 类
* @module toast
* @version 0.4.0
* @version 0.5.0
*/
/**
* Toast 类 — 核心通知组件
@@ -2827,12 +2827,19 @@
if (!this._hooks.has(name))
this._hooks.set(name, []);
this._hooks.get(name).push(fn);
return () => this.off(name, fn);
// 用类名引用而非 this,保证返回的取消函数在任意调用上下文可用
return () => Toast.off(name, fn);
}
static off(name, fn) {
const list = this._hooks.get(name);
if (list)
this._hooks.set(name, list.filter(f => f !== fn));
if (list) {
const next = list.filter(f => f !== fn);
// 空数组直接删除 key,避免残留
if (next.length === 0)
this._hooks.delete(name);
else
this._hooks.set(name, next);
}
}
/**
* 触发钩子 — 如果任意钩子返回 false 则整体返回 false
@@ -3440,7 +3447,7 @@
/**
* MetonaToast Templates — HTML 模板辅助函数
* @module templates
* @version 0.4.0
* @version 0.5.0
* @description confirm / prompt / progress / action 的 DOM 模板
*/
/**
@@ -3512,7 +3519,7 @@
/**
* MetonaToast Plugins — 插件系统
* @module plugins
* @version 0.4.0
* @version 0.5.0
*/
/**
* 插件管理器
@@ -3667,6 +3674,12 @@
}
catch (_e) { /* noop */ }
}
// 卸载 use() 注册的配置保存钩子
const meta = this;
if (typeof meta._saveOff === 'function') {
meta._saveOff();
meta._saveOff = null;
}
},
},
/**
@@ -3689,6 +3702,14 @@
setTimeout(() => { if (el.parentNode)
el.parentNode.removeChild(el); }, 3000);
},
uninstall() {
// 卸载 use() 注册的朗读钩子
const meta = this;
if (typeof meta._off === 'function') {
meta._off();
meta._off = null;
}
},
},
/**
* 去重插件 — 相同 type + message 的 toast 自动合并(更新已有的一条)
@@ -3758,7 +3779,7 @@
/**
* MetonaToast API — meToast 核心 API 对象
* @module api
* @version 0.4.0
* @version 0.5.0
*/
/**
* 参数标准化工具
@@ -4094,12 +4115,14 @@
actions.forEach((a, i) => {
const btn = toast.el?.querySelector(`.met-action-btn-${i}`);
if (btn && typeof a.onClick === 'function') {
btn.addEventListener('click', () => {
btn.addEventListener('click', (e) => {
// 阻止冒泡到 toast 的 closeOnClick 处理器,由 close 选项控制是否关闭
e.stopPropagation();
try {
a.onClick(toast);
}
catch (e) {
console.error('Action onClick error:', e);
catch (err) {
console.error('Action onClick error:', err);
}
if (a.close !== false)
toast.close();
@@ -4336,25 +4359,37 @@
return this;
}
this.plugins.register(plugin, { ...preset, ...options });
// 连接插件钩子
// 连接插件钩子(重复 use 前先卸载旧钩子,防止重复注册)
// 注意:off 引用存在已注册的插件对象上(stored),uninstall 时 this 即该对象
const stored = this.plugins.get(plugin);
if (plugin === 'accessibility') {
Toast.on('afterShow', (toast) => {
const acc = preset;
if (typeof acc.announce === 'function')
acc.announce(toast);
});
if (stored && typeof stored._off === 'function') {
stored._off();
}
const acc = preset;
if (stored) {
stored._off = Toast.on('afterShow', (toast) => {
if (typeof acc.announce === 'function')
acc.announce(toast);
});
}
}
if (plugin === 'persistence') {
if (stored && typeof stored._saveOff === 'function') {
stored._saveOff();
}
const saved = typeof preset.install === 'function'
? preset.install(this.plugins)
: null;
if (saved)
this.configure(saved);
Toast.on('afterClose', () => {
const p = preset;
if (typeof p.save === 'function')
p.save(this.getConfig());
});
const p = preset;
if (stored) {
stored._saveOff = Toast.on('afterClose', () => {
if (typeof p.save === 'function')
p.save(this.getConfig());
});
}
}
}
else if (plugin && typeof plugin === 'object') {
@@ -4430,7 +4465,7 @@
/**
* MetonaToast — 轻量级Toast通知库
* @module metona-toast
* @version 0.4.0
* @version 0.5.0
* @author thzxx
* @license MIT
*/
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+72 -37
View File
@@ -5,7 +5,7 @@ var react = require('react');
/**
* MetonaToast Utils 工具函数
* @module utils
* @version 0.4.0
* @version 0.5.0
*/
/**
* 生成唯一ID
@@ -42,7 +42,7 @@ const prefersDark = () => {
/**
* MetonaToast Icons 图标SVG定义
* @module icons
* @version 0.4.0
* @version 0.5.0
* @description 107 个内置 SVG 图标
*/
const ICONS = {
@@ -539,7 +539,7 @@ const ICONS = {
/**
* MetonaToast Locales 国际化翻译数据
* @module locales
* @version 0.4.0
* @version 0.5.0
* @description 内置 zh-CN / en-US 完整翻译
*/
const LOCALES = {
@@ -1026,12 +1026,12 @@ const LOCALES = {
/**
* MetonaToast Constants 常量定义
* @module constants
* @version 0.4.0
* @version 0.5.0
*/
/**
* 版本号 唯一来源发布时只需修改此处
*/
const VERSION = '0.4.0';
const VERSION = '0.5.0';
/**
* 默认配置
*/
@@ -1298,7 +1298,7 @@ const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', '
/**
* MetonaToast Styles 样式管理
* @module styles
* @version 0.4.0
* @version 0.5.0
*/
// 样式缓存
let styleElement = null;
@@ -1787,7 +1787,7 @@ const injectStyles = () => {
/**
* MetonaToast Themes 主题管理
* @module themes
* @version 0.4.0
* @version 0.5.0
*/
// 当前主题状态
let currentTheme = 'auto';
@@ -2131,7 +2131,7 @@ const themeUtils = {
/**
* MetonaToast i18n 国际化管理
* @module i18n
* @version 0.4.0
* @version 0.5.0
*/
// 当前语言状态
let currentLocale = 'zh-CN';
@@ -2499,7 +2499,7 @@ const formatNumber = (number, options = {}) => {
try {
return new Intl.NumberFormat(currentLocale, options).format(number);
}
catch (e) {
catch {
return number.toString();
}
};
@@ -2514,7 +2514,7 @@ const formatCurrency = (amount, currency = 'USD', options = {}) => {
...options,
}).format(amount);
}
catch (e) {
catch {
return amount.toString();
}
};
@@ -2530,7 +2530,7 @@ const formatPercent = (value, options = {}) => {
...options,
}).format(value / 100);
}
catch (e) {
catch {
return `${value}%`;
}
};
@@ -2542,7 +2542,7 @@ const formatDate = (date, options = {}) => {
const dateObj = date instanceof Date ? date : new Date(date);
return new Intl.DateTimeFormat(currentLocale, options).format(dateObj);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2586,7 +2586,7 @@ const formatRelativeTime = (date, options = {}) => {
}
return String(date);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2613,7 +2613,7 @@ const formatPlural = (count, options = {}) => {
try {
return new Intl.PluralRules(currentLocale, options).select(count);
}
catch (e) {
catch {
return count === 1 ? 'one' : 'other';
}
};
@@ -2806,7 +2806,7 @@ const animationUtils = {
/**
* MetonaToast Toast Toast
* @module toast
* @version 0.4.0
* @version 0.5.0
*/
/**
* Toast 核心通知组件
@@ -2825,12 +2825,19 @@ let Toast$1 = class Toast {
if (!this._hooks.has(name))
this._hooks.set(name, []);
this._hooks.get(name).push(fn);
return () => this.off(name, fn);
// 用类名引用而非 this,保证返回的取消函数在任意调用上下文可用
return () => Toast.off(name, fn);
}
static off(name, fn) {
const list = this._hooks.get(name);
if (list)
this._hooks.set(name, list.filter(f => f !== fn));
if (list) {
const next = list.filter(f => f !== fn);
// 空数组直接删除 key,避免残留
if (next.length === 0)
this._hooks.delete(name);
else
this._hooks.set(name, next);
}
}
/**
* 触发钩子 如果任意钩子返回 false 则整体返回 false
@@ -3438,7 +3445,7 @@ const _containerCache = new Map();
/**
* MetonaToast Templates HTML 模板辅助函数
* @module templates
* @version 0.4.0
* @version 0.5.0
* @description confirm / prompt / progress / action DOM 模板
*/
/**
@@ -3510,7 +3517,7 @@ const actionHTML = (actions) => {
/**
* MetonaToast Plugins 插件系统
* @module plugins
* @version 0.4.0
* @version 0.5.0
*/
/**
* 插件管理器
@@ -3665,6 +3672,12 @@ const presetPlugins = {
}
catch (_e) { /* noop */ }
}
// 卸载 use() 注册的配置保存钩子
const meta = this;
if (typeof meta._saveOff === 'function') {
meta._saveOff();
meta._saveOff = null;
}
},
},
/**
@@ -3687,6 +3700,14 @@ const presetPlugins = {
setTimeout(() => { if (el.parentNode)
el.parentNode.removeChild(el); }, 3000);
},
uninstall() {
// 卸载 use() 注册的朗读钩子
const meta = this;
if (typeof meta._off === 'function') {
meta._off();
meta._off = null;
}
},
},
/**
* 去重插件 相同 type + message toast 自动合并更新已有的一条
@@ -3756,7 +3777,7 @@ const defaultPluginManager = new PluginManager();
/**
* MetonaToast API meToast 核心 API 对象
* @module api
* @version 0.4.0
* @version 0.5.0
*/
/**
* 参数标准化工具
@@ -4092,12 +4113,14 @@ const meToast = {
actions.forEach((a, i) => {
const btn = toast.el?.querySelector(`.met-action-btn-${i}`);
if (btn && typeof a.onClick === 'function') {
btn.addEventListener('click', () => {
btn.addEventListener('click', (e) => {
// 阻止冒泡到 toast 的 closeOnClick 处理器,由 close 选项控制是否关闭
e.stopPropagation();
try {
a.onClick(toast);
}
catch (e) {
console.error('Action onClick error:', e);
catch (err) {
console.error('Action onClick error:', err);
}
if (a.close !== false)
toast.close();
@@ -4334,25 +4357,37 @@ const meToast = {
return this;
}
this.plugins.register(plugin, { ...preset, ...options });
// 连接插件钩子
// 连接插件钩子(重复 use 前先卸载旧钩子,防止重复注册)
// 注意:off 引用存在已注册的插件对象上(stored),uninstall 时 this 即该对象
const stored = this.plugins.get(plugin);
if (plugin === 'accessibility') {
Toast$1.on('afterShow', (toast) => {
const acc = preset;
if (typeof acc.announce === 'function')
acc.announce(toast);
});
if (stored && typeof stored._off === 'function') {
stored._off();
}
const acc = preset;
if (stored) {
stored._off = Toast$1.on('afterShow', (toast) => {
if (typeof acc.announce === 'function')
acc.announce(toast);
});
}
}
if (plugin === 'persistence') {
if (stored && typeof stored._saveOff === 'function') {
stored._saveOff();
}
const saved = typeof preset.install === 'function'
? preset.install(this.plugins)
: null;
if (saved)
this.configure(saved);
Toast$1.on('afterClose', () => {
const p = preset;
if (typeof p.save === 'function')
p.save(this.getConfig());
});
const p = preset;
if (stored) {
stored._saveOff = Toast$1.on('afterClose', () => {
if (typeof p.save === 'function')
p.save(this.getConfig());
});
}
}
}
else if (plugin && typeof plugin === 'object') {
@@ -4428,7 +4463,7 @@ Toast$1.setCallbacks({
/**
* MetonaToast 轻量级Toast通知库
* @module metona-toast
* @version 0.4.0
* @version 0.5.0
* @author thzxx
* @license MIT
*/
@@ -4494,7 +4529,7 @@ if (typeof window !== 'undefined') {
/**
* MetonaToast React React 适配器
* @module react
* @version 0.4.0
* @version 0.5.0
* @description useToast hook + 声明式 <Toast /> 组件主包保持零依赖React optional peerDependency
*/
const SHOW_METHODS = ['success', 'error', 'warning', 'info', 'show'];
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,7 +1,7 @@
/**
* MetonaToast
* @module types
* @version 0.4.0
* @version 0.5.0
*/
/** Toast 通知类型(107种内置图标类型) */
type ToastType = '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';
@@ -478,7 +478,7 @@ declare const enhancedMeToast: {
/**
* MetonaToast React React
* @module react
* @version 0.4.0
* @version 0.5.0
* @description useToast hook + <Toast /> React optional peerDependency
*/
+72 -37
View File
@@ -3,7 +3,7 @@ import { useRef, useEffect, useMemo } from 'react';
/**
* MetonaToast Utils 工具函数
* @module utils
* @version 0.4.0
* @version 0.5.0
*/
/**
* 生成唯一ID
@@ -40,7 +40,7 @@ const prefersDark = () => {
/**
* MetonaToast Icons 图标SVG定义
* @module icons
* @version 0.4.0
* @version 0.5.0
* @description 107 个内置 SVG 图标
*/
const ICONS = {
@@ -537,7 +537,7 @@ const ICONS = {
/**
* MetonaToast Locales 国际化翻译数据
* @module locales
* @version 0.4.0
* @version 0.5.0
* @description 内置 zh-CN / en-US 完整翻译
*/
const LOCALES = {
@@ -1024,12 +1024,12 @@ const LOCALES = {
/**
* MetonaToast Constants 常量定义
* @module constants
* @version 0.4.0
* @version 0.5.0
*/
/**
* 版本号 唯一来源发布时只需修改此处
*/
const VERSION = '0.4.0';
const VERSION = '0.5.0';
/**
* 默认配置
*/
@@ -1296,7 +1296,7 @@ const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', '
/**
* MetonaToast Styles 样式管理
* @module styles
* @version 0.4.0
* @version 0.5.0
*/
// 样式缓存
let styleElement = null;
@@ -1785,7 +1785,7 @@ const injectStyles = () => {
/**
* MetonaToast Themes 主题管理
* @module themes
* @version 0.4.0
* @version 0.5.0
*/
// 当前主题状态
let currentTheme = 'auto';
@@ -2129,7 +2129,7 @@ const themeUtils = {
/**
* MetonaToast i18n 国际化管理
* @module i18n
* @version 0.4.0
* @version 0.5.0
*/
// 当前语言状态
let currentLocale = 'zh-CN';
@@ -2497,7 +2497,7 @@ const formatNumber = (number, options = {}) => {
try {
return new Intl.NumberFormat(currentLocale, options).format(number);
}
catch (e) {
catch {
return number.toString();
}
};
@@ -2512,7 +2512,7 @@ const formatCurrency = (amount, currency = 'USD', options = {}) => {
...options,
}).format(amount);
}
catch (e) {
catch {
return amount.toString();
}
};
@@ -2528,7 +2528,7 @@ const formatPercent = (value, options = {}) => {
...options,
}).format(value / 100);
}
catch (e) {
catch {
return `${value}%`;
}
};
@@ -2540,7 +2540,7 @@ const formatDate = (date, options = {}) => {
const dateObj = date instanceof Date ? date : new Date(date);
return new Intl.DateTimeFormat(currentLocale, options).format(dateObj);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2584,7 +2584,7 @@ const formatRelativeTime = (date, options = {}) => {
}
return String(date);
}
catch (e) {
catch {
return String(date);
}
};
@@ -2611,7 +2611,7 @@ const formatPlural = (count, options = {}) => {
try {
return new Intl.PluralRules(currentLocale, options).select(count);
}
catch (e) {
catch {
return count === 1 ? 'one' : 'other';
}
};
@@ -2804,7 +2804,7 @@ const animationUtils = {
/**
* MetonaToast Toast Toast
* @module toast
* @version 0.4.0
* @version 0.5.0
*/
/**
* Toast 核心通知组件
@@ -2823,12 +2823,19 @@ let Toast$1 = class Toast {
if (!this._hooks.has(name))
this._hooks.set(name, []);
this._hooks.get(name).push(fn);
return () => this.off(name, fn);
// 用类名引用而非 this,保证返回的取消函数在任意调用上下文可用
return () => Toast.off(name, fn);
}
static off(name, fn) {
const list = this._hooks.get(name);
if (list)
this._hooks.set(name, list.filter(f => f !== fn));
if (list) {
const next = list.filter(f => f !== fn);
// 空数组直接删除 key,避免残留
if (next.length === 0)
this._hooks.delete(name);
else
this._hooks.set(name, next);
}
}
/**
* 触发钩子 如果任意钩子返回 false 则整体返回 false
@@ -3436,7 +3443,7 @@ const _containerCache = new Map();
/**
* MetonaToast Templates HTML 模板辅助函数
* @module templates
* @version 0.4.0
* @version 0.5.0
* @description confirm / prompt / progress / action DOM 模板
*/
/**
@@ -3508,7 +3515,7 @@ const actionHTML = (actions) => {
/**
* MetonaToast Plugins 插件系统
* @module plugins
* @version 0.4.0
* @version 0.5.0
*/
/**
* 插件管理器
@@ -3663,6 +3670,12 @@ const presetPlugins = {
}
catch (_e) { /* noop */ }
}
// 卸载 use() 注册的配置保存钩子
const meta = this;
if (typeof meta._saveOff === 'function') {
meta._saveOff();
meta._saveOff = null;
}
},
},
/**
@@ -3685,6 +3698,14 @@ const presetPlugins = {
setTimeout(() => { if (el.parentNode)
el.parentNode.removeChild(el); }, 3000);
},
uninstall() {
// 卸载 use() 注册的朗读钩子
const meta = this;
if (typeof meta._off === 'function') {
meta._off();
meta._off = null;
}
},
},
/**
* 去重插件 相同 type + message toast 自动合并更新已有的一条
@@ -3754,7 +3775,7 @@ const defaultPluginManager = new PluginManager();
/**
* MetonaToast API meToast 核心 API 对象
* @module api
* @version 0.4.0
* @version 0.5.0
*/
/**
* 参数标准化工具
@@ -4090,12 +4111,14 @@ const meToast = {
actions.forEach((a, i) => {
const btn = toast.el?.querySelector(`.met-action-btn-${i}`);
if (btn && typeof a.onClick === 'function') {
btn.addEventListener('click', () => {
btn.addEventListener('click', (e) => {
// 阻止冒泡到 toast 的 closeOnClick 处理器,由 close 选项控制是否关闭
e.stopPropagation();
try {
a.onClick(toast);
}
catch (e) {
console.error('Action onClick error:', e);
catch (err) {
console.error('Action onClick error:', err);
}
if (a.close !== false)
toast.close();
@@ -4332,25 +4355,37 @@ const meToast = {
return this;
}
this.plugins.register(plugin, { ...preset, ...options });
// 连接插件钩子
// 连接插件钩子(重复 use 前先卸载旧钩子,防止重复注册)
// 注意:off 引用存在已注册的插件对象上(stored),uninstall 时 this 即该对象
const stored = this.plugins.get(plugin);
if (plugin === 'accessibility') {
Toast$1.on('afterShow', (toast) => {
const acc = preset;
if (typeof acc.announce === 'function')
acc.announce(toast);
});
if (stored && typeof stored._off === 'function') {
stored._off();
}
const acc = preset;
if (stored) {
stored._off = Toast$1.on('afterShow', (toast) => {
if (typeof acc.announce === 'function')
acc.announce(toast);
});
}
}
if (plugin === 'persistence') {
if (stored && typeof stored._saveOff === 'function') {
stored._saveOff();
}
const saved = typeof preset.install === 'function'
? preset.install(this.plugins)
: null;
if (saved)
this.configure(saved);
Toast$1.on('afterClose', () => {
const p = preset;
if (typeof p.save === 'function')
p.save(this.getConfig());
});
const p = preset;
if (stored) {
stored._saveOff = Toast$1.on('afterClose', () => {
if (typeof p.save === 'function')
p.save(this.getConfig());
});
}
}
}
else if (plugin && typeof plugin === 'object') {
@@ -4426,7 +4461,7 @@ Toast$1.setCallbacks({
/**
* MetonaToast 轻量级Toast通知库
* @module metona-toast
* @version 0.4.0
* @version 0.5.0
* @author thzxx
* @license MIT
*/
@@ -4492,7 +4527,7 @@ if (typeof window !== 'undefined') {
/**
* MetonaToast React React 适配器
* @module react
* @version 0.4.0
* @version 0.5.0
* @description useToast hook + 声明式 <Toast /> 组件主包保持零依赖React optional peerDependency
*/
const SHOW_METHODS = ['success', 'error', 'warning', 'info', 'show'];
+1 -1
View File
File diff suppressed because one or more lines are too long