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
+552
View File
@@ -0,0 +1,552 @@
/**
* MetonaToast — 核心类型定义
* @module types
* @version 0.2.0
*/
// ========== 基础类型 ==========
/** Toast 通知类型(107种内置图标类型) */
export 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';
/** Toast 显示位置 */
export type ToastPosition =
| 'top-left' | 'top-center' | 'top-right'
| 'bottom-left' | 'bottom-center' | 'bottom-right';
/** Toast 主题 */
export type ToastTheme = 'light' | 'dark' | 'auto' | 'warm' | (string & {});
/** Toast 动画类型 */
export type ToastAnimation =
| 'slide' | 'fade' | 'scale' | 'bounce' | 'flip' | 'rotate' | 'zoom'
| 'slideUp' | 'slideDown' | 'slideLeft' | 'slideRight'
| (string & {});
/** 进度条方向 */
export type ToastProgressDirection = 'horizontal' | 'vertical';
/** 语言代码 */
export type ToastLocale =
| 'zh-CN' | 'zh-TW' | 'en-US' | 'en-GB' | 'ja' | 'ko' | 'fr' | 'de'
| 'es' | 'pt' | 'ru' | 'ar' | 'hi' | 'th' | 'vi' | 'id' | 'ms' | 'tr'
| 'it' | 'nl' | 'pl' | 'uk' | 'cs' | 'sv' | 'da' | 'fi' | 'nb' | 'el'
| 'he' | 'hu' | 'ro' | 'bg' | 'hr' | 'sk' | 'sl' | 'et' | 'lv' | 'lt'
| 'ca' | 'gl' | 'eu' | 'cy' | 'ga' | 'mt' | 'is' | 'mk' | 'sq' | 'sr'
| 'bs' | 'me' | 'ka' | 'hy' | 'az' | 'uz' | 'kk' | 'ky' | 'tg' | 'tk'
| 'mn' | 'ne' | 'si' | 'my' | 'km' | 'lo' | 'am' | 'sw' | 'yo' | 'ig'
| 'ha' | 'zu' | 'af' | 'xh' | 'st' | 'tn' | 'ts' | 'ss' | 've' | 'nr';
// ========== 颜色配置 ==========
export interface TypeColor {
fg: string;
bg: string;
}
// ========== Toast 配置 ==========
export interface ToastConfig {
position?: ToastPosition;
duration?: number;
max?: number;
gap?: number;
offset?: number;
pauseOnHover?: boolean;
closeOnClick?: boolean;
draggable?: boolean;
showProgress?: boolean;
progressDirection?: ToastProgressDirection;
icon?: boolean;
closeButton?: boolean;
theme?: ToastTheme;
animation?: ToastAnimation;
zIndex?: number;
width?: number | string;
className?: string;
style?: Record<string, string>;
onShow?: ((toast: ToastInstance) => void) | null;
onClose?: ((toast: ToastInstance) => void) | null;
onClick?: ((toast: ToastInstance) => void) | null;
onUpdate?: ((toast: ToastInstance) => void) | null;
render?: ((toast: ToastInstance) => string) | null;
resetTimerOnUpdate?: boolean;
notifyWhenHidden?: boolean;
onError?: ((errorInfo: ErrorInfo) => void) | null;
group?: string | null;
locale?: string;
plugins?: Array<string | Plugin>;
}
export interface ErrorInfo {
hook?: string;
source?: string;
error: Error;
toast?: ToastInstance;
}
// ========== Toast 选项与实例 ==========
export interface ToastOptions extends Partial<ToastConfig> {
type?: string;
title?: string;
message?: string;
content?: string;
html?: string;
iconHTML?: string;
id?: string;
}
export interface ToastInstance {
id: string;
type: string;
title: string;
message: string;
html: string;
iconHTML: string;
config: ToastConfig;
el: HTMLElement | null;
barEl: HTMLElement | null;
rafId: number | null;
remaining: number;
startedAt: number;
paused: boolean;
closing: boolean;
group: string | null;
_cleanups: Array<() => void>;
_palette(): { theme: string; c: TypeColor; t: Partial<ThemeConfig> };
create(): this;
update(partial: Partial<ToastOptions>): this;
close(immediate?: boolean): void;
_pause(): void;
_resume(): void;
_destroy(): void;
}
// ========== 控制对象 ==========
export interface LoadingControl {
id: string;
success(message?: string, opts?: ToastOptions): ToastInstance | null;
error(message?: string, opts?: ToastOptions): ToastInstance | null;
info(message?: string, opts?: ToastOptions): ToastInstance | null;
warning(message?: string, opts?: ToastOptions): ToastInstance | null;
update(partial: Partial<ToastOptions>): unknown;
dismiss(): void;
}
export interface ProgressControl {
id: string;
setProgress(percent: number): void;
complete(message?: string): void;
error(message?: string): void;
dismiss(): void;
}
export interface CountdownControl {
id: string;
cancel(): void;
pause(): void;
resume(): void;
}
export interface ActionControl {
id: string;
toast: ToastInstance;
dismiss(): void;
}
export interface QueueControl {
then<TResult1 = void, TResult2 = never>(
onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2>;
catch<TResult = never>(
onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
): Promise<void | TResult>;
cancel(): void;
}
export interface GroupAPI {
_group: string;
show: (...args: unknown[]) => ToastInstance;
success: (...args: unknown[]) => ToastInstance;
error: (...args: unknown[]) => ToastInstance;
warning: (...args: unknown[]) => ToastInstance;
info: (...args: unknown[]) => ToastInstance;
loading: (...args: unknown[]) => LoadingControl;
action: (...args: unknown[]) => ActionControl;
dismiss(): void;
count(): number;
}
// ========== Action 按钮 ==========
export interface ActionButton {
text: string;
onClick: (toast: ToastInstance) => void;
color?: string;
style?: Record<string, string>;
close?: boolean;
}
// ========== Options 接口 ==========
export interface PromiseOptions extends ToastOptions {
loading?: string;
success?: string;
error?: string;
}
export interface ConfirmOptions extends ToastOptions {
confirmText?: string;
confirmColor?: string;
cancelText?: string;
cancelColor?: string;
}
export interface PromptOptions extends ToastOptions {
inputType?: string;
placeholder?: string;
defaultValue?: string;
submitText?: string;
submitColor?: string;
cancelText?: string;
cancelColor?: string;
}
export interface ProgressOptions extends ToastOptions {
progressColor?: string;
}
export interface CountdownOptions extends ToastOptions {
onComplete?: (() => void) | null;
}
export interface QueueOptions extends ToastOptions {
delay?: number;
}
export interface StackOptions extends ToastOptions {
stagger?: number;
}
export interface InitOptions {
config?: Partial<ToastConfig>;
theme?: ToastTheme;
locale?: string;
plugins?: Array<string | Plugin>;
}
export interface StatusInfo {
version: string;
toasts: number;
theme: string;
locale: string;
plugins: string[];
animations: number;
}
// ========== 动画 ==========
export interface AnimationConfig {
enter: Record<string, string>;
leave: Record<string, string>;
duration: number;
easing: string;
name?: string;
delay?: number;
iterations?: number;
direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
fillMode?: 'none' | 'forwards' | 'backwards' | 'both';
}
export interface AnimationUtils {
register(name: string, config: Partial<AnimationConfig>): void;
unregister(name: string): void;
get(name: string): AnimationConfig | null;
getAnimationNames(): string[];
getActiveCount(): number;
cancelAll(): void;
reset(): void;
destroy(): void;
}
// ========== 主题 ==========
export interface ThemeConfig {
bg: string;
text: string;
border: string;
shadow: string;
hoverShadow: string;
progressBg: string;
closeHoverBg: string;
backdropFilter?: string;
}
export interface ThemePreview {
name: string;
resolved: string;
colors: {
background: string;
text: string;
border: string;
};
isDark: boolean;
isLight: boolean;
isAuto: boolean;
}
export interface ThemeUtils {
getSystemTheme(): 'light' | 'dark';
resolveTheme(theme: string): string;
getThemeConfig(theme: string): ThemeConfig;
applyTheme(theme: string): void;
getCurrentTheme(): string;
getResolvedTheme(): string;
switchTheme(theme: string): void;
toggleTheme(): void;
resetToAuto(): void;
initTheme(): void;
watchSystemTheme(): void;
unwatchSystemTheme(): void;
addThemeListener(listener: (theme: string, resolved: string) => void): () => void;
removeThemeListener(listener: (theme: string, resolved: string) => void): void;
clearThemeListeners(): void;
registerTheme(name: string, config: ThemeConfig): void;
unregisterTheme(name: string): void;
getAllThemes(): Record<string, ThemeConfig>;
getThemeNames(): string[];
hasTheme(name: string): boolean;
getThemePreview(theme: string): ThemePreview;
generateThemeCSS(theme: string): string;
applyThemeCSS(theme: string): void;
removeThemeCSS(): void;
saveTheme(theme: string): void;
loadTheme(): string;
}
// ========== 国际化 ==========
export interface LocaleInfo {
code: string;
name: string;
direction: 'ltr' | 'rtl';
isSupported: boolean;
isCurrent: boolean;
isFallback: boolean;
}
export interface I18nUtils {
t(key: string, params?: Record<string, string | number>): string;
plural(key: string, count: number, params?: Record<string, string | number>): string;
getCurrentLocale(): string;
setCurrentLocale(locale: string): void;
switchLocale(locale: string): void;
getFallbackLocale(): string;
setFallbackLocale(locale: string): void;
hasTranslation(key: string): boolean;
getTranslations(locale: string): Record<string, unknown>;
addTranslations(locale: string, translations: Record<string, unknown>): void;
removeTranslation(locale: string, key: string): void;
clearTranslations(locale: string): void;
getSupportedLocales(): string[];
isLocaleSupported(locale: string): boolean;
getLocaleName(locale: string): string;
getLocaleDirection(locale: string): 'ltr' | 'rtl';
getLocaleInfo(locale: string): LocaleInfo;
getAllLocaleInfo(): LocaleInfo[];
formatNumber(number: number, options?: Intl.NumberFormatOptions): string;
formatCurrency(amount: number, currency?: string, options?: Intl.NumberFormatOptions): string;
formatPercent(value: number, options?: Intl.NumberFormatOptions): string;
formatDate(date: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
formatTime(date: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
formatRelativeTime(date: Date | number | string, options?: Intl.RelativeTimeFormatOptions): string;
formatList(list: string[], options?: Record<string, unknown>): string;
formatPlural(count: number, options?: Intl.PluralRulesOptions): string;
addLocaleListener(listener: (locale: string) => void): () => void;
removeLocaleListener(listener: (locale: string) => void): void;
clearLocaleListeners(): void;
initI18n(): void;
saveLocale(locale: string): void;
loadLocale(): string;
getDefaultLocale(): string;
}
// ========== 插件 ==========
export interface Plugin {
name: string;
version?: string;
description?: string;
hooks?: Record<string, (...args: unknown[]) => void>;
middleware?: (...args: unknown[]) => unknown;
install?: (manager: PluginManager) => void | Record<string, unknown> | null;
uninstall?: (manager: PluginManager) => void;
init?: (manager: PluginManager) => void;
destroy?: (manager: PluginManager) => void;
installed?: boolean;
enabled?: boolean;
[key: string]: unknown;
}
export interface PluginManager {
register(name: string, plugin: Plugin): PluginManager;
unregister(name: string): PluginManager;
get(name: string): Plugin | null;
has(name: string): boolean;
getAll(): Plugin[];
getNames(): string[];
enable(name: string): PluginManager;
disable(name: string): PluginManager;
isEnabled(name: string): boolean;
destroy(): void;
}
export interface PluginUtils {
createManager(): PluginManager;
register(name: string, plugin: Plugin): PluginManager;
unregister(name: string): PluginManager;
get(name: string): Plugin | null;
has(name: string): boolean;
getAll(): Plugin[];
getNames(): string[];
enable(name: string): PluginManager;
disable(name: string): PluginManager;
isEnabled(name: string): boolean;
getPreset(name: string): Plugin | null;
getAllPresets(): Record<string, Plugin>;
createPlugin(config: Partial<Plugin>): Plugin;
validatePlugin(plugin: Partial<Plugin>): { valid: boolean; errors: string[] };
}
// ========== MeToast 主接口 ==========
export interface MeToast {
version: string;
_toasts: Map<string, ToastInstance>;
_config: ToastConfig;
_destroyed?: boolean;
// 内部方法
_emit(opts: ToastOptions): ToastInstance;
_remove(id: string): void;
_resolve(loadingToast: ToastInstance, type: string, message: string, opts: ToastOptions): ToastInstance | null;
_groupCount(name: string): number;
// 配置
configure(opts: Partial<ToastConfig>): MeToast;
init(options?: InitOptions): MeToast;
destroy(): void;
getStatus(): StatusInfo;
getConfig(): ToastConfig;
updateConfig(config: Partial<ToastConfig>): MeToast;
resetConfig(): MeToast;
// Toast 方法
show(messageOrOpts: string | ToastOptions, opts?: ToastOptions): ToastInstance;
success(messageOrOpts: string | ToastOptions, opts?: ToastOptions): ToastInstance;
error(messageOrOpts: string | ToastOptions, opts?: ToastOptions): ToastInstance;
warning(messageOrOpts: string | ToastOptions, opts?: ToastOptions): ToastInstance;
info(messageOrOpts: string | ToastOptions, opts?: ToastOptions): ToastInstance;
loading(messageOrOpts: string | ToastOptions, opts?: ToastOptions): LoadingControl;
promise<T>(promise: Promise<T>, opts?: PromiseOptions): Promise<T>;
confirm(message: string, opts?: ConfirmOptions): Promise<boolean>;
prompt(message: string, opts?: PromptOptions): Promise<string | null>;
progress(messageOrOpts: string | ToastOptions, opts?: ProgressOptions): ProgressControl;
countdown(message: string, seconds?: number, opts?: CountdownOptions): CountdownControl;
action(messageOrOpts: string | ToastOptions, actions?: ActionButton[], opts?: ToastOptions): ActionControl;
queue(messages: Array<string | ToastOptions>, opts?: QueueOptions): QueueControl;
stack(messages: Array<string | ToastOptions>, opts?: StackOptions): void;
// 分组
group(name: string): GroupAPI;
dismissGroup(name: string): void;
// Toast 管理
find(id: string): ToastInstance | undefined;
dismiss(id?: string): void;
clear(position?: ToastPosition): void;
getToasts(): ToastInstance[];
count(): number;
hasToasts(): boolean;
getToast(id: string): ToastInstance | null;
closeAll(): void;
clearAll(): void;
pauseAll(): void;
resumeAll(): void;
updateAll(partial: Partial<ToastOptions>): void;
findToasts(predicate: (toast: ToastInstance) => boolean): ToastInstance[];
findByType(type: ToastType): ToastInstance[];
findByPosition(position: ToastPosition): ToastInstance[];
getAll(): Map<string, ToastInstance>;
// 插件
use(plugin: string | Plugin, options?: Record<string, unknown>): MeToast;
// 子模块
animations: AnimationUtils;
themes: ThemeUtils;
i18n: I18nUtils;
plugins: PluginUtils;
presetPlugins: Record<string, Plugin>;
}
// ========== 钩子名称 ==========
export const HOOK_NAMES = {
BEFORE_INIT: 'beforeInit',
AFTER_INIT: 'afterInit',
BEFORE_DESTROY: 'beforeDestroy',
AFTER_DESTROY: 'afterDestroy',
BEFORE_SHOW: 'beforeShow',
AFTER_SHOW: 'afterShow',
BEFORE_CLOSE: 'beforeClose',
AFTER_CLOSE: 'afterClose',
BEFORE_UPDATE: 'beforeUpdate',
AFTER_UPDATE: 'afterUpdate',
CONFIG_CHANGE: 'configChange',
THEME_CHANGE: 'themeChange',
LOCALE_CHANGE: 'localeChange',
CLICK: 'click',
HOVER: 'hover',
DRAG_START: 'dragStart',
DRAG_END: 'dragEnd',
ANIMATION_START: 'animationStart',
ANIMATION_END: 'animationEnd',
PROGRESS_START: 'progressStart',
PROGRESS_UPDATE: 'progressUpdate',
PROGRESS_END: 'progressEnd',
ERROR: 'error',
CUSTOM: 'custom',
} as const;
export type HookName = (typeof HOOK_NAMES)[keyof typeof HOOK_NAMES];
// ========== 全局声明 ==========
declare global {
interface Window {
MeToast: MeToast;
Met: MeToast;
}
}