release: v0.2.0 — TypeScript 源码重构
### 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:
+339
@@ -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';
|
||||
Reference in New Issue
Block a user