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:
+617
@@ -0,0 +1,617 @@
|
||||
/**
|
||||
* MetonaToast API — meToast 核心 API 对象
|
||||
* @module api
|
||||
* @version 0.2.0
|
||||
*/
|
||||
|
||||
import { Toast, _containerCache } from './toast.js';
|
||||
import { DEFAULTS } from './constants.js';
|
||||
import { escapeHTML } from './utils.js';
|
||||
import { t } from './i18n.js';
|
||||
import { applyTheme } from './themes.js';
|
||||
import { setCurrentLocale } from './i18n.js';
|
||||
import { confirmHTML, promptHTML, progressHTML, actionHTML } from './templates.js';
|
||||
import type {
|
||||
ToastConfig, ToastOptions, ToastInstance,
|
||||
LoadingControl, ProgressControl, CountdownControl,
|
||||
ActionControl, QueueControl, GroupAPI,
|
||||
ActionButton, PromiseOptions, ConfirmOptions,
|
||||
PromptOptions, ProgressOptions, CountdownOptions,
|
||||
QueueOptions, StackOptions, MeToast, ErrorInfo,
|
||||
} from './types.js';
|
||||
|
||||
/**
|
||||
* 参数标准化工具
|
||||
*/
|
||||
const normalizeArgs = (args: unknown[], defaultType = 'default'): ToastOptions => {
|
||||
const [first, second] = args;
|
||||
|
||||
if (typeof first === 'string') {
|
||||
return {
|
||||
...((second as Record<string, unknown>) || {}),
|
||||
type: (second as Record<string, unknown>)?.type as string || defaultType,
|
||||
message: first,
|
||||
};
|
||||
}
|
||||
|
||||
if (first && typeof first === 'object') {
|
||||
return {
|
||||
...(first as Record<string, unknown>),
|
||||
type: (first as Record<string, unknown>).type as string || defaultType,
|
||||
} as ToastOptions;
|
||||
}
|
||||
|
||||
return {
|
||||
type: defaultType,
|
||||
message: '',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* meToast API 对象
|
||||
*/
|
||||
const meToast: MeToast = {
|
||||
_toasts: new Map<string, ToastInstance>(),
|
||||
_config: { ...DEFAULTS } as unknown as ToastConfig,
|
||||
version: '0.2.0',
|
||||
|
||||
configure(opts: Partial<ToastConfig>): MeToast {
|
||||
if (!opts || typeof opts !== 'object') return this;
|
||||
Object.assign(this._config, opts);
|
||||
|
||||
if (opts.theme) {
|
||||
applyTheme(opts.theme);
|
||||
}
|
||||
|
||||
if (opts.locale) {
|
||||
setCurrentLocale(opts.locale);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_emit(opts: ToastOptions): Toast {
|
||||
const merged: ToastOptions = { ...this._config, ...opts };
|
||||
if (merged.content && !merged.message) merged.message = merged.content;
|
||||
|
||||
const t = new Toast(merged);
|
||||
t.create();
|
||||
this._toasts.set(t.id, t);
|
||||
|
||||
return t;
|
||||
},
|
||||
|
||||
_remove(id: string): void {
|
||||
this._toasts.delete(id);
|
||||
},
|
||||
|
||||
find(id: string): ToastInstance | undefined {
|
||||
if (!id) return undefined;
|
||||
return this._toasts.get(id);
|
||||
},
|
||||
|
||||
show(messageOrOpts: string | ToastOptions, opts: ToastOptions = {}): ToastInstance {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'default');
|
||||
return this._emit(normalized);
|
||||
},
|
||||
|
||||
success(messageOrOpts: string | ToastOptions, opts: ToastOptions = {}): ToastInstance {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'success');
|
||||
return this._emit(normalized);
|
||||
},
|
||||
|
||||
error(messageOrOpts: string | ToastOptions, opts: ToastOptions = {}): ToastInstance {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'error');
|
||||
return this._emit(normalized);
|
||||
},
|
||||
|
||||
warning(messageOrOpts: string | ToastOptions, opts: ToastOptions = {}): ToastInstance {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'warning');
|
||||
return this._emit(normalized);
|
||||
},
|
||||
|
||||
info(messageOrOpts: string | ToastOptions, opts: ToastOptions = {}): ToastInstance {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'info');
|
||||
return this._emit(normalized);
|
||||
},
|
||||
|
||||
loading(messageOrOpts: string | ToastOptions, opts: ToastOptions = {}): LoadingControl {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'loading');
|
||||
normalized.duration = 0;
|
||||
normalized.closeButton = false;
|
||||
normalized.showProgress = false;
|
||||
|
||||
const toast = this._emit(normalized);
|
||||
|
||||
return {
|
||||
id: toast.id,
|
||||
success: (msg?: string, o: ToastOptions = {}) => this._resolve(toast, 'success', msg || t('success'), o),
|
||||
error: (msg?: string, o: ToastOptions = {}) => this._resolve(toast, 'error', msg || t('error'), o),
|
||||
info: (msg?: string, o: ToastOptions = {}) => this._resolve(toast, 'info', msg || t('info'), o),
|
||||
warning: (msg?: string, o: ToastOptions = {}) => this._resolve(toast, 'warning', msg || t('warning'), o),
|
||||
update: (p: Partial<ToastOptions>) => { toast.update(p); return this; },
|
||||
dismiss: () => toast.close(),
|
||||
};
|
||||
},
|
||||
|
||||
promise<T>(promise: Promise<T>, opts: PromiseOptions = {}): Promise<T> {
|
||||
if (!promise || typeof (promise as unknown as { then?: unknown }).then !== 'function') {
|
||||
console.error('MeToast.promise: first argument must be a Promise');
|
||||
return Promise.reject(new Error('Invalid promise'));
|
||||
}
|
||||
|
||||
const loadingMsg = opts.loading || t('loading');
|
||||
const successMsg = opts.success || t('success');
|
||||
const errorMsg = opts.error || t('error');
|
||||
|
||||
const ctrl = this.loading(loadingMsg);
|
||||
|
||||
return Promise.resolve(promise)
|
||||
.then((data: T) => {
|
||||
ctrl.success(successMsg);
|
||||
return data;
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
ctrl.error(errorMsg);
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
||||
_resolve(loadingToast: ToastInstance, type: string, message: string, opts: ToastOptions): ToastInstance | null {
|
||||
const id = loadingToast.id;
|
||||
const old = this._toasts.get(id);
|
||||
if (!old) return null;
|
||||
|
||||
const position = old.config.position;
|
||||
old.close();
|
||||
|
||||
return this._emit({ ...opts, type, message, position } as ToastOptions);
|
||||
},
|
||||
|
||||
confirm(message: string, opts: ConfirmOptions = {}): Promise<boolean> {
|
||||
if (typeof message !== 'string') {
|
||||
console.error('MeToast.confirm: message must be a string');
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
let resolved = false;
|
||||
const safeResolve = (val: boolean) => {
|
||||
if (!resolved) { resolved = true; clearTimeout(safetyTimeout); resolve(val); }
|
||||
};
|
||||
const safetyTimeout = setTimeout(() => safeResolve(false), 10000);
|
||||
|
||||
const toast = this._emit({
|
||||
...opts,
|
||||
type: opts.type || 'warning',
|
||||
message,
|
||||
duration: 0,
|
||||
closeButton: false,
|
||||
closeOnClick: false,
|
||||
draggable: false,
|
||||
html: confirmHTML(opts),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
const confirmBtn = toast.el?.querySelector('.met-confirm-btn') as HTMLElement | null;
|
||||
const cancelBtn = toast.el?.querySelector('.met-cancel-btn') as HTMLElement | null;
|
||||
|
||||
if (confirmBtn) {
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
toast.close();
|
||||
safeResolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
toast.close();
|
||||
safeResolve(false);
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
|
||||
prompt(message: string, opts: PromptOptions = {}): Promise<string | null> {
|
||||
if (typeof message !== 'string') {
|
||||
console.error('MeToast.prompt: message must be a string');
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return new Promise<string | null>((resolve) => {
|
||||
let resolved = false;
|
||||
const safeResolve = (val: string | null) => {
|
||||
if (!resolved) { resolved = true; clearTimeout(safetyTimeout); resolve(val); }
|
||||
};
|
||||
const safetyTimeout = setTimeout(() => safeResolve(null), 10000);
|
||||
|
||||
const toast = this._emit({
|
||||
...opts,
|
||||
type: opts.type || 'info',
|
||||
message,
|
||||
duration: 0,
|
||||
closeButton: false,
|
||||
closeOnClick: false,
|
||||
draggable: false,
|
||||
html: promptHTML(opts),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
const input = toast.el?.querySelector('.met-input') as HTMLInputElement | null;
|
||||
const submitBtn = toast.el?.querySelector('.met-submit-btn') as HTMLElement | null;
|
||||
const cancelBtn = toast.el?.querySelector('.met-cancel-btn') as HTMLElement | null;
|
||||
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
toast.close();
|
||||
safeResolve(input.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (submitBtn) {
|
||||
submitBtn.addEventListener('click', () => {
|
||||
toast.close();
|
||||
safeResolve(input?.value || null);
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
toast.close();
|
||||
safeResolve(null);
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
|
||||
progress(messageOrOpts: string | ToastOptions, opts: ProgressOptions = {}): ProgressControl {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'info');
|
||||
normalized.duration = 0;
|
||||
normalized.closeButton = false;
|
||||
normalized.showProgress = false;
|
||||
|
||||
const toast = this._emit({
|
||||
...normalized,
|
||||
html: progressHTML(opts),
|
||||
});
|
||||
|
||||
return {
|
||||
id: toast.id,
|
||||
|
||||
setProgress(percent: number) {
|
||||
const fill = toast.el?.querySelector('.met-progress-fill') as HTMLElement | null;
|
||||
const text = toast.el?.querySelector('.met-progress-text') as HTMLElement | null;
|
||||
|
||||
if (fill) {
|
||||
fill.style.width = `${Math.min(100, Math.max(0, percent))}%`;
|
||||
}
|
||||
if (text) {
|
||||
text.textContent = `${Math.round(percent)}%`;
|
||||
}
|
||||
},
|
||||
|
||||
complete(message?: string) {
|
||||
this.setProgress(100);
|
||||
setTimeout(() => {
|
||||
toast.update({
|
||||
type: 'success',
|
||||
message: message || t('success'),
|
||||
html: '',
|
||||
});
|
||||
setTimeout(() => toast.close(), 1000);
|
||||
}, 300);
|
||||
},
|
||||
|
||||
error(message?: string) {
|
||||
toast.update({
|
||||
type: 'error',
|
||||
message: message || t('error'),
|
||||
html: '',
|
||||
});
|
||||
setTimeout(() => toast.close(), 2000);
|
||||
},
|
||||
|
||||
dismiss() {
|
||||
toast.close();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
countdown(message: string, seconds = 10, opts: CountdownOptions = {}): CountdownControl {
|
||||
if (typeof message !== 'string') {
|
||||
console.error('MeToast.countdown: message must be a string');
|
||||
return { id: '', cancel: () => {}, pause: () => {}, resume: () => {} };
|
||||
}
|
||||
|
||||
let remaining = Math.max(1, parseInt(String(seconds)) || 10);
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
const toast = this._emit({
|
||||
...opts,
|
||||
type: opts.type || 'warning',
|
||||
message: message.replace(/\{seconds\}/g, String(remaining)),
|
||||
duration: 0,
|
||||
closeButton: true,
|
||||
showProgress: false,
|
||||
});
|
||||
|
||||
const tick = (): void => {
|
||||
remaining--;
|
||||
|
||||
if (remaining <= 0) {
|
||||
if (timer !== null) clearInterval(timer);
|
||||
toast.close();
|
||||
|
||||
if (typeof opts.onComplete === 'function') {
|
||||
try { opts.onComplete(); } catch (e) { console.error('countdown onComplete error:', e); }
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
toast.update({
|
||||
message: message.replace(/\{seconds\}/g, String(remaining)),
|
||||
});
|
||||
};
|
||||
|
||||
timer = setInterval(tick, 1000);
|
||||
|
||||
return {
|
||||
id: toast.id,
|
||||
cancel() { if (timer !== null) clearInterval(timer); toast.close(); },
|
||||
pause() { if (timer !== null) clearInterval(timer); },
|
||||
resume() { if (timer !== null) clearInterval(timer); timer = setInterval(tick, 1000); },
|
||||
};
|
||||
},
|
||||
|
||||
action(messageOrOpts: string | ToastOptions, actions: ActionButton[] = [], opts: ToastOptions = {}): ActionControl {
|
||||
const normalized = normalizeArgs([messageOrOpts, opts], 'info');
|
||||
normalized.duration = opts.duration ?? 0;
|
||||
normalized.closeButton = opts.closeButton ?? true;
|
||||
|
||||
const toast = this._emit({
|
||||
...normalized,
|
||||
html: (normalized.html || '') + actionHTML(actions),
|
||||
});
|
||||
|
||||
if (Array.isArray(actions)) {
|
||||
setTimeout(() => {
|
||||
actions.forEach((a, i) => {
|
||||
const btn = toast.el?.querySelector(`.met-action-btn-${i}`) as HTMLElement | null;
|
||||
if (btn && typeof a.onClick === 'function') {
|
||||
btn.addEventListener('click', () => {
|
||||
try { a.onClick(toast); } catch (e) { console.error('Action onClick error:', e); }
|
||||
if (a.close !== false) toast.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return { id: toast.id, toast, dismiss: () => toast.close() };
|
||||
},
|
||||
|
||||
queue(messages: Array<string | ToastOptions>, opts: QueueOptions = {}): QueueControl {
|
||||
if (!Array.isArray(messages)) {
|
||||
console.error('MeToast.queue: messages must be an array');
|
||||
const p = Promise.resolve();
|
||||
return { then: (fn) => p.then(fn), catch: (rj) => p.catch(rj), cancel: () => {} };
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
const promise = new Promise<void>((resolve) => {
|
||||
let index = 0;
|
||||
const delay = opts.delay || 1000;
|
||||
const userOnClose = opts.onClose;
|
||||
|
||||
const showNext = (): void => {
|
||||
if (cancelled || index >= messages.length) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const message = messages[index];
|
||||
index++;
|
||||
|
||||
const msg = typeof message === 'string' ? message : ((message as ToastOptions)?.message || '');
|
||||
const msgObj: ToastOptions = (typeof message === 'object' && message !== null) ? message as ToastOptions : {};
|
||||
const msgOnClose = msgObj.onClose;
|
||||
|
||||
this._emit({
|
||||
...opts,
|
||||
...msgObj,
|
||||
message: msg,
|
||||
duration: msgObj.duration || opts.duration || 3000,
|
||||
onClose: (toast: ToastInstance) => {
|
||||
if (typeof msgOnClose === 'function') {
|
||||
try { msgOnClose(toast); } catch (_e) { /* noop */ }
|
||||
}
|
||||
if (typeof userOnClose === 'function') {
|
||||
try { userOnClose(toast); } catch (_e) { /* noop */ }
|
||||
}
|
||||
setTimeout(showNext, delay);
|
||||
},
|
||||
} as ToastOptions);
|
||||
};
|
||||
|
||||
showNext();
|
||||
});
|
||||
|
||||
return {
|
||||
then: (fn, rj) => promise.then(fn, rj),
|
||||
catch: (rj) => promise.catch(rj),
|
||||
cancel: () => { cancelled = true; },
|
||||
};
|
||||
},
|
||||
|
||||
stack(messages: Array<string | ToastOptions>, opts: StackOptions = {}): void {
|
||||
if (!Array.isArray(messages)) {
|
||||
console.error('MeToast.stack: messages must be an array');
|
||||
return;
|
||||
}
|
||||
|
||||
messages.forEach((message, index) => {
|
||||
setTimeout(() => {
|
||||
const msg = typeof message === 'string' ? message : ((message as ToastOptions)?.message || '');
|
||||
|
||||
this._emit({
|
||||
...opts,
|
||||
...((typeof message === 'object' && message !== null) ? message as ToastOptions : {}),
|
||||
message: msg,
|
||||
} as ToastOptions);
|
||||
}, index * (opts.stagger || 100));
|
||||
});
|
||||
},
|
||||
|
||||
dismiss(id?: string): void {
|
||||
if (id) {
|
||||
const t = this._toasts.get(id);
|
||||
if (t) t.close();
|
||||
return;
|
||||
}
|
||||
this._toasts.forEach(t => t.close());
|
||||
},
|
||||
|
||||
clear(position?: string): void {
|
||||
this._toasts.forEach(t => {
|
||||
if (!position || t.config.position === position) t.close();
|
||||
});
|
||||
},
|
||||
|
||||
destroy(): void {
|
||||
this.dismiss();
|
||||
|
||||
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();
|
||||
_containerCache.clear();
|
||||
},
|
||||
|
||||
getAll(): Map<string, ToastInstance> {
|
||||
return new Map(this._toasts);
|
||||
},
|
||||
|
||||
count(): number {
|
||||
return this._toasts.size;
|
||||
},
|
||||
|
||||
group(name: string): GroupAPI {
|
||||
const self = this;
|
||||
const methods = ['show', 'success', 'error', 'warning', 'info', 'loading', 'action'];
|
||||
const g: GroupAPI = { _group: name } as unknown as GroupAPI;
|
||||
methods.forEach(m => {
|
||||
(g as unknown as Record<string, (...args: unknown[]) => unknown>)[m] = (...args: unknown[]) => {
|
||||
const lastArg = args[args.length - 1];
|
||||
const isObj = typeof lastArg === 'object' && lastArg !== null && !Array.isArray(lastArg);
|
||||
const selfMethods = self as unknown as Record<string, (...a: unknown[]) => unknown>;
|
||||
if (isObj && args.length === 1) {
|
||||
return selfMethods[m]({ ...(lastArg as Record<string, unknown>), group: name });
|
||||
}
|
||||
if (isObj) {
|
||||
args.pop();
|
||||
return selfMethods[m](...args, { ...(lastArg as Record<string, unknown>), group: name });
|
||||
}
|
||||
return selfMethods[m](...args, { group: name });
|
||||
};
|
||||
});
|
||||
g.dismiss = () => self.dismissGroup(name);
|
||||
g.count = () => self._groupCount(name);
|
||||
return g;
|
||||
},
|
||||
|
||||
dismissGroup(name: string): void {
|
||||
this._toasts.forEach(t => { if (t.group === name) t.close(); });
|
||||
},
|
||||
|
||||
_groupCount(name: string): number {
|
||||
let c = 0;
|
||||
this._toasts.forEach(t => { if (t.group === name) c++; });
|
||||
return c;
|
||||
},
|
||||
|
||||
getToasts(): ToastInstance[] {
|
||||
return Array.from(this._toasts.values());
|
||||
},
|
||||
|
||||
hasToasts(): boolean {
|
||||
return this._toasts.size > 0;
|
||||
},
|
||||
|
||||
getToast(id: string): ToastInstance | null {
|
||||
if (!id) return null;
|
||||
return this._toasts.get(id) || null;
|
||||
},
|
||||
|
||||
closeAll(): void {
|
||||
this._toasts.forEach(t => t.close());
|
||||
},
|
||||
|
||||
clearAll(): void {
|
||||
this._toasts.forEach(t => t.close());
|
||||
},
|
||||
|
||||
pauseAll(): void {
|
||||
this._toasts.forEach(t => t._pause());
|
||||
},
|
||||
|
||||
resumeAll(): void {
|
||||
this._toasts.forEach(t => t._resume());
|
||||
},
|
||||
|
||||
updateAll(partial: Partial<ToastOptions>): void {
|
||||
if (partial && typeof partial === 'object') {
|
||||
this._toasts.forEach(t => t.update(partial));
|
||||
}
|
||||
},
|
||||
|
||||
findToasts(predicate: (toast: ToastInstance) => boolean): ToastInstance[] {
|
||||
if (typeof predicate !== 'function') return [];
|
||||
return Array.from(this._toasts.values()).filter(predicate);
|
||||
},
|
||||
|
||||
findByType(type: string): ToastInstance[] {
|
||||
return this.findToasts(t => t.type === type);
|
||||
},
|
||||
|
||||
findByPosition(position: string): ToastInstance[] {
|
||||
return this.findToasts(t => t.config.position === position);
|
||||
},
|
||||
|
||||
// ====== 以下由 index.ts 增强 ======
|
||||
|
||||
init(_options?: Record<string, unknown>): MeToast { return this; },
|
||||
getStatus() { return { version: '0.2.0', toasts: 0, theme: '', locale: '', plugins: [], animations: 0 }; },
|
||||
getConfig(): ToastConfig { return { ...this._config }; },
|
||||
updateConfig(_config: Partial<ToastConfig>): MeToast { return this; },
|
||||
resetConfig(): MeToast { return this; },
|
||||
use(_plugin: string | Record<string, unknown>, _options?: Record<string, unknown>): MeToast { return this; },
|
||||
|
||||
animations: null as unknown as MeToast['animations'],
|
||||
themes: null as unknown as MeToast['themes'],
|
||||
i18n: null as unknown as MeToast['i18n'],
|
||||
plugins: null as unknown as MeToast['plugins'],
|
||||
presetPlugins: {} as MeToast['presetPlugins'],
|
||||
};
|
||||
|
||||
// 连接 Toast 静态回调到 meToast 实例
|
||||
Toast._onError = (info: ErrorInfo) => {
|
||||
const onError = meToast._config.onError;
|
||||
if (typeof onError === 'function') {
|
||||
try { onError(info); } catch (_e) { /* noop */ }
|
||||
}
|
||||
};
|
||||
Toast._removeToast = (id: string) => {
|
||||
meToast._toasts.delete(id);
|
||||
};
|
||||
|
||||
export { meToast as default, meToast };
|
||||
Reference in New Issue
Block a user