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:
Vendored
+72
-37
@@ -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'];
|
||||
|
||||
Reference in New Issue
Block a user