release: v0.2.0 — TypeScript 源码重构
CI / test (18.x) (push) Successful in 9m52s
CI / test (22.x) (push) Canceled after 0s
CI / test (24.x) (push) Canceled after 0s
CI / test (20.x) (push) Canceled after 2m49s

### Changed
- 全部源码从 JavaScript 迁移到 TypeScript (strict mode)
- core.js (1244行) 拆分为 toast.ts + api.ts + templates.ts
- 删除手动维护的 types/index.d.ts,类型从源码自动生成
- 构建工具链: Babel → ts-jest, 新增 @rollup/plugin-typescript
- 新增 rollup-plugin-dts 生成合并 .d.ts

### Added
- tsconfig.json (strict: true)
- src/types.ts 核心类型模块 (39 个导出类型)
- .eslintrc.json (@typescript-eslint)
- .gitea/workflows/ci.yml (Node 18/20/22/24 矩阵)
- CHANGELOG.md

### Aligned with metona-starter
- package.json: type:module, engines≥16, prepublishOnly
- rollup.config.js: dts plugin, port 3001
- jest.config.cjs, build.sh, serve.sh, .gitignore (.npmrc)

### Removed
- babel.config.js, types/ 目录, 所有 src/*.js
This commit is contained in:
tianhao
2026-07-25 11:22:07 +08:00
parent ee2b685f64
commit 9a6cd33d90
36 changed files with 3673 additions and 5473 deletions
+339
View File
@@ -0,0 +1,339 @@
/**
* MetonaToast — 轻量级Toast通知库
* @module metona-toast
* @version 0.2.0
* @author thzxx
* @license MIT
*/
import { meToast } from './api.js';
import { Toast } from './toast.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';
import type { MeToast, ToastConfig, InitOptions, StatusInfo, ToastInstance, ToastOptions, Plugin } from './types.js';
// 版本信息
const VERSION = '0.2.0';
/**
* 主对象增强
*/
const enhancedMeToast: MeToast = {
...meToast,
version: VERSION,
animations: animationUtils,
themes: themeUtils,
i18n: i18nUtils,
plugins: pluginUtils,
presetPlugins,
/**
* 安装插件
*/
use(plugin: string | Record<string, unknown>, options: Record<string, unknown> = {}): MeToast {
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: ToastInstance) => {
if (typeof preset.announce === 'function') preset.announce(toast);
});
}
if (plugin === 'persistence') {
const saved = typeof preset.install === 'function' ? preset.install(pluginUtils as unknown as import('./types.js').PluginManager) : null;
if (saved) this.configure(saved as Partial<ToastConfig>);
Toast.on('afterClose', () => {
if (typeof (preset as Record<string, unknown>).save === 'function') {
(preset as Record<string, (c: unknown) => void>).save(this.getConfig());
}
});
}
} else if (plugin && typeof plugin === 'object') {
const name = (plugin as Record<string, string>).name || 'custom';
this.plugins.register(name, { ...plugin, ...options } as unknown as Plugin);
}
return this;
},
/**
* 初始化
*/
init(options: InitOptions = {}): MeToast {
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((p) => {
this.use(p as string);
});
}
return this;
},
/**
* 销毁
*/
destroy(): void {
if (this._destroyed) return;
this._destroyed = true;
this.dismiss();
if (this.plugins && typeof (this.plugins as unknown as Record<string, unknown>).destroy === 'function') {
(this.plugins as unknown as Record<string, () => void>).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) => { if (c.parentNode) c.parentNode.removeChild(c); });
const style = document.getElementById('metona-toast-styles');
if (style && style.parentNode) style.parentNode.removeChild(style);
}
this._toasts.clear();
},
/**
* 获取状态
*/
getStatus(): StatusInfo {
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(): ToastConfig {
return { ...this._config };
},
/**
* 更新配置
*/
updateConfig(config: Partial<ToastConfig>): MeToast {
if (config && typeof config === 'object') {
Object.assign(this._config, config);
}
return this;
},
/**
* 重置配置
*/
resetConfig(): MeToast {
const keys = Object.keys(this._config);
keys.forEach(k => delete (this._config as Record<string, unknown>)[k]);
Object.assign(this._config, DEFAULTS);
return this;
},
/**
* 获取Toast列表
*/
getToasts(): ToastInstance[] {
return Array.from(this._toasts.values());
},
/**
* 检查是否有Toast
*/
hasToasts(): boolean {
return this._toasts.size > 0;
},
/**
* 获取Toast
*/
getToast(id: string): ToastInstance | null {
if (!id) return null;
return this._toasts.get(id) || null;
},
/**
* 关闭所有Toast
*/
closeAll(): void {
this._toasts.forEach((t) => t.close());
},
/**
* 清除所有Toast
*/
clearAll(): void {
this._toasts.forEach((t) => t.close());
},
/**
* 暂停所有Toast
*/
pauseAll(): void {
this._toasts.forEach((t) => t._pause());
},
/**
* 恢复所有Toast
*/
resumeAll(): void {
this._toasts.forEach((t) => t._resume());
},
/**
* 更新所有Toast
*/
updateAll(partial: Partial<ToastOptions>): void {
if (partial && typeof partial === 'object') {
this._toasts.forEach((t) => t.update(partial));
}
},
/**
* 查找Toast
*/
findToasts(predicate: (toast: ToastInstance) => boolean): ToastInstance[] {
if (typeof predicate !== 'function') return [];
return Array.from(this._toasts.values()).filter(predicate);
},
/**
* 按类型查找Toast
*/
findByType(type: string): ToastInstance[] {
return this.findToasts((t) => t.type === type);
},
/**
* 按位置查找Toast
*/
findByPosition(position: string): ToastInstance[] {
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;
// Notification API — 页面不可见时自动发送系统通知
const sendNotification = (toast: ToastInstance): void => {
try {
new Notification(toast.title || toast.type, { body: toast.message });
} catch (_e) { /* noop */ }
};
(enhancedMeToast as unknown as Record<string, unknown>)._notify = (toast: ToastInstance) => {
if (typeof Notification === 'undefined') return;
if (Notification.permission === 'denied') return;
if (!document.hidden) return;
if (Notification.permission === 'default') {
Notification.requestPermission().then(p => { if (p === 'granted') sendNotification(toast); });
return;
}
sendNotification(toast);
};
Toast.on('afterShow', (toast: ToastInstance) => {
const config = toast.config as ToastConfig;
if (config.notifyWhenHidden) {
((enhancedMeToast as unknown as Record<string, (t: ToastInstance) => void>)._notify)(toast);
}
});
}
// 导出
export default enhancedMeToast;
export { enhancedMeToast as meToast, enhancedMeToast as Met, enhancedMeToast as MeToast, Toast, VERSION };
// 类型重导出(供 TypeScript 消费者 import type 使用)
export type {
ToastType,
ToastPosition,
ToastTheme,
ToastAnimation,
ToastProgressDirection,
ToastConfig,
ToastOptions,
ToastInstance,
TypeColor,
ErrorInfo,
LoadingControl,
ProgressControl,
CountdownControl,
ActionControl,
QueueControl,
GroupAPI,
ActionButton,
PromiseOptions,
ConfirmOptions,
PromptOptions,
ProgressOptions,
CountdownOptions,
QueueOptions,
StackOptions,
InitOptions,
StatusInfo,
AnimationConfig,
AnimationUtils,
ThemeConfig,
ThemePreview,
ThemeUtils,
LocaleInfo,
I18nUtils,
Plugin,
PluginManager,
PluginUtils,
} from './types.js';