init: MetonaToast v2.0.0 基线 — 已完成改进(模板抽象/constants拆分/测试增强)
This commit is contained in:
@@ -0,0 +1,860 @@
|
||||
/**
|
||||
* MetonaToast Animations - 动画管理
|
||||
* @module animations
|
||||
* @version 2.0.0
|
||||
* @description 动画效果管理和自定义
|
||||
*/
|
||||
|
||||
import { ANIMATIONS } from './constants.js';
|
||||
|
||||
// 动画缓存
|
||||
const animationCache = new Map();
|
||||
|
||||
/**
|
||||
* 动画管理器类
|
||||
*/
|
||||
class AnimationManager {
|
||||
constructor() {
|
||||
this.animations = new Map();
|
||||
this.activeAnimations = new Map();
|
||||
this.animationId = 0;
|
||||
|
||||
// 注册默认动画
|
||||
this._registerDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册默认动画
|
||||
*/
|
||||
_registerDefaults() {
|
||||
Object.entries(ANIMATIONS).forEach(([name, config]) => {
|
||||
this.register(name, config);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册动画
|
||||
* @param {string} name - 动画名称
|
||||
* @param {Object} config - 动画配置
|
||||
*/
|
||||
register(name, config) {
|
||||
const animation = {
|
||||
name,
|
||||
enter: config.enter || {},
|
||||
leave: config.leave || {},
|
||||
duration: config.duration || 300,
|
||||
easing: config.easing || 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
delay: config.delay || 0,
|
||||
iterations: config.iterations || 1,
|
||||
direction: config.direction || 'normal',
|
||||
fillMode: config.fillMode || 'forwards',
|
||||
};
|
||||
|
||||
this.animations.set(name, animation);
|
||||
animationCache.set(name, animation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销动画
|
||||
* @param {string} name - 动画名称
|
||||
*/
|
||||
unregister(name) {
|
||||
this.animations.delete(name);
|
||||
animationCache.delete(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @param {string} name - 动画名称
|
||||
* @returns {Object|null} 动画配置
|
||||
*/
|
||||
get(name) {
|
||||
return this.animations.get(name) || ANIMATIONS[name] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
apply(element, animationName, options = {}) {
|
||||
return new Promise((resolve) => {
|
||||
const animation = this.get(animationName);
|
||||
if (!animation) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const config = { ...animation, ...options };
|
||||
const animId = ++this.animationId;
|
||||
|
||||
// 设置初始状态
|
||||
Object.assign(element.style, config.enter);
|
||||
|
||||
// 强制重绘
|
||||
element.offsetHeight;
|
||||
|
||||
// 创建动画
|
||||
const keyframes = [
|
||||
{ ...config.enter },
|
||||
{ ...config.leave },
|
||||
];
|
||||
|
||||
const animationOptions = {
|
||||
duration: config.duration,
|
||||
easing: config.easing,
|
||||
delay: config.delay,
|
||||
iterations: config.iterations,
|
||||
direction: config.direction,
|
||||
fill: config.fillMode,
|
||||
};
|
||||
|
||||
// 应用动画
|
||||
const anim = element.animate(keyframes, animationOptions);
|
||||
|
||||
// 存储活动动画
|
||||
this.activeAnimations.set(animId, {
|
||||
element,
|
||||
animation: anim,
|
||||
config,
|
||||
});
|
||||
|
||||
// 动画完成处理
|
||||
anim.onfinish = () => {
|
||||
// 应用最终状态
|
||||
Object.assign(element.style, config.leave);
|
||||
|
||||
// 清理
|
||||
this.activeAnimations.delete(animId);
|
||||
|
||||
resolve();
|
||||
};
|
||||
|
||||
// 动画取消处理
|
||||
anim.oncancel = () => {
|
||||
this.activeAnimations.delete(animId);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用进入动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
enter(element, animationName, options = {}) {
|
||||
const animation = this.get(animationName);
|
||||
if (!animation) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.apply(element, animationName, {
|
||||
...options,
|
||||
direction: 'normal',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用离开动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
leave(element, animationName, options = {}) {
|
||||
const animation = this.get(animationName);
|
||||
if (!animation) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.apply(element, animationName, {
|
||||
...options,
|
||||
direction: 'reverse',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有动画
|
||||
*/
|
||||
cancelAll() {
|
||||
this.activeAnimations.forEach(({ animation }) => {
|
||||
animation.cancel();
|
||||
});
|
||||
|
||||
this.activeAnimations.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
cancel(element) {
|
||||
this.activeAnimations.forEach(({ element: el, animation }, id) => {
|
||||
if (el === element) {
|
||||
animation.cancel();
|
||||
this.activeAnimations.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停所有动画
|
||||
*/
|
||||
pauseAll() {
|
||||
this.activeAnimations.forEach(({ animation }) => {
|
||||
animation.pause();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复所有动画
|
||||
*/
|
||||
resumeAll() {
|
||||
this.activeAnimations.forEach(({ animation }) => {
|
||||
animation.play();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
pause(element) {
|
||||
this.activeAnimations.forEach(({ element: el, animation }) => {
|
||||
if (el === element) {
|
||||
animation.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
resume(element) {
|
||||
this.activeAnimations.forEach(({ element: el, animation }) => {
|
||||
if (el === element) {
|
||||
animation.play();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动动画数量
|
||||
* @returns {number} 动画数量
|
||||
*/
|
||||
getActiveCount() {
|
||||
return this.activeAnimations.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有活动动画
|
||||
* @returns {Map} 活动动画映射
|
||||
*/
|
||||
getActiveAnimations() {
|
||||
return new Map(this.activeAnimations);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查元素是否有活动动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @returns {boolean} 是否有活动动画
|
||||
*/
|
||||
hasActiveAnimation(element) {
|
||||
for (const { element: el } of this.activeAnimations.values()) {
|
||||
if (el === element) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @returns {Object} 动画配置
|
||||
*/
|
||||
getConfig() {
|
||||
return {
|
||||
animations: Array.from(this.animations.keys()),
|
||||
activeCount: this.getActiveCount(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置动画管理器
|
||||
*/
|
||||
reset() {
|
||||
this.cancelAll();
|
||||
this.animations.clear();
|
||||
this._registerDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁动画管理器
|
||||
*/
|
||||
destroy() {
|
||||
this.cancelAll();
|
||||
this.animations.clear();
|
||||
this.activeAnimations.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预设动画效果
|
||||
*/
|
||||
const presetAnimations = {
|
||||
bounce: {
|
||||
enter: { transform: 'translateY(-80px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(20px)', opacity: 1 },
|
||||
duration: 650,
|
||||
easing: 'ease',
|
||||
},
|
||||
slideUp: {
|
||||
enter: { transform: 'translateY(60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideDown: {
|
||||
enter: { transform: 'translateY(-60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideLeft: {
|
||||
enter: { transform: 'translateX(-90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideRight: {
|
||||
enter: { transform: 'translateX(90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(0)', opacity: 1 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// 注册预设动画
|
||||
Object.entries(presetAnimations).forEach(([name, config]) => {
|
||||
ANIMATIONS[name] = config;
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建动画管理器实例
|
||||
* @returns {AnimationManager} 动画管理器实例
|
||||
*/
|
||||
export const createAnimationManager = () => {
|
||||
return new AnimationManager();
|
||||
};
|
||||
|
||||
// 创建默认实例
|
||||
const defaultAnimationManager = createAnimationManager();
|
||||
|
||||
/**
|
||||
* 动画工具函数
|
||||
*/
|
||||
export const animationUtils = {
|
||||
/**
|
||||
* 应用动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
apply(element, animationName, options = {}) {
|
||||
return defaultAnimationManager.apply(element, animationName, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用进入动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
enter(element, animationName, options = {}) {
|
||||
return defaultAnimationManager.enter(element, animationName, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用离开动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @param {string} animationName - 动画名称
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
leave(element, animationName, options = {}) {
|
||||
return defaultAnimationManager.leave(element, animationName, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消所有动画
|
||||
*/
|
||||
cancelAll() {
|
||||
defaultAnimationManager.cancelAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
cancel(element) {
|
||||
defaultAnimationManager.cancel(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂停所有动画
|
||||
*/
|
||||
pauseAll() {
|
||||
defaultAnimationManager.pauseAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复所有动画
|
||||
*/
|
||||
resumeAll() {
|
||||
defaultAnimationManager.resumeAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂停元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
pause(element) {
|
||||
defaultAnimationManager.pause(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复元素动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
*/
|
||||
resume(element) {
|
||||
defaultAnimationManager.resume(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取活动动画数量
|
||||
* @returns {number} 动画数量
|
||||
*/
|
||||
getActiveCount() {
|
||||
return defaultAnimationManager.getActiveCount();
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查元素是否有活动动画
|
||||
* @param {HTMLElement} element - 目标元素
|
||||
* @returns {boolean} 是否有活动动画
|
||||
*/
|
||||
hasActiveAnimation(element) {
|
||||
return defaultAnimationManager.hasActiveAnimation(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注册动画
|
||||
* @param {string} name - 动画名称
|
||||
* @param {Object} config - 动画配置
|
||||
*/
|
||||
register(name, config) {
|
||||
defaultAnimationManager.register(name, config);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注销动画
|
||||
* @param {string} name - 动画名称
|
||||
*/
|
||||
unregister(name) {
|
||||
defaultAnimationManager.unregister(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @param {string} name - 动画名称
|
||||
* @returns {Object|null} 动画配置
|
||||
*/
|
||||
get(name) {
|
||||
return defaultAnimationManager.get(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有动画名称
|
||||
* @returns {Array} 动画名称数组
|
||||
*/
|
||||
getAnimationNames() {
|
||||
return Array.from(defaultAnimationManager.animations.keys());
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取动画配置
|
||||
* @returns {Object} 动画配置
|
||||
*/
|
||||
getConfig() {
|
||||
return defaultAnimationManager.getConfig();
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置动画管理器
|
||||
*/
|
||||
reset() {
|
||||
defaultAnimationManager.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* 销毁动画管理器
|
||||
*/
|
||||
destroy() {
|
||||
defaultAnimationManager.destroy();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画预设
|
||||
*/
|
||||
export const animationPresets = presetAnimations;
|
||||
|
||||
/**
|
||||
* 创建自定义动画
|
||||
* @param {Object} config - 动画配置
|
||||
* @returns {Object} 动画配置
|
||||
*/
|
||||
export const createAnimation = (config) => {
|
||||
return {
|
||||
enter: config.enter || {},
|
||||
leave: config.leave || {},
|
||||
duration: config.duration || 300,
|
||||
easing: config.easing || 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
delay: config.delay || 0,
|
||||
iterations: config.iterations || 1,
|
||||
direction: config.direction || 'normal',
|
||||
fillMode: config.fillMode || 'forwards',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 组合动画
|
||||
* @param {Array} animations - 动画数组
|
||||
* @returns {Object} 组合后的动画配置
|
||||
*/
|
||||
export const combineAnimations = (animations) => {
|
||||
const combined = {
|
||||
enter: {},
|
||||
leave: {},
|
||||
duration: 0,
|
||||
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
delay: 0,
|
||||
iterations: 1,
|
||||
direction: 'normal',
|
||||
fillMode: 'forwards',
|
||||
};
|
||||
|
||||
animations.forEach((anim) => {
|
||||
if (anim.enter) {
|
||||
Object.assign(combined.enter, anim.enter);
|
||||
}
|
||||
if (anim.leave) {
|
||||
Object.assign(combined.leave, anim.leave);
|
||||
}
|
||||
if (anim.duration) {
|
||||
combined.duration = Math.max(combined.duration, anim.duration);
|
||||
}
|
||||
if (anim.easing) {
|
||||
combined.easing = anim.easing;
|
||||
}
|
||||
if (anim.delay) {
|
||||
combined.delay = Math.max(combined.delay, anim.delay);
|
||||
}
|
||||
});
|
||||
|
||||
return combined;
|
||||
};
|
||||
|
||||
/**
|
||||
* 链式动画
|
||||
* @param {Array} animations - 动画数组
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
export const chainAnimations = async (element, animations) => {
|
||||
for (const anim of animations) {
|
||||
await defaultAnimationManager.apply(element, anim.name, anim.options);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 并行动画
|
||||
* @param {Array} animations - 动画数组
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
export const parallelAnimations = (element, animations) => {
|
||||
return Promise.all(
|
||||
animations.map((anim) =>
|
||||
defaultAnimationManager.apply(element, anim.name, anim.options)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 延迟动画
|
||||
* @param {number} delay - 延迟时间(毫秒)
|
||||
* @returns {Promise} Promise对象
|
||||
*/
|
||||
export const delayAnimation = (delay) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, delay));
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画队列
|
||||
*/
|
||||
export class AnimationQueue {
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
this.isProcessing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加动画到队列
|
||||
* @param {Function} animationFn - 动画函数
|
||||
* @returns {Promise} 动画完成Promise
|
||||
*/
|
||||
add(animationFn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.queue.push({
|
||||
fn: animationFn,
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
|
||||
this._process();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理队列
|
||||
*/
|
||||
async _process() {
|
||||
if (this.isProcessing || this.queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProcessing = true;
|
||||
|
||||
while (this.queue.length > 0) {
|
||||
const { fn, resolve, reject } = this.queue.shift();
|
||||
|
||||
try {
|
||||
const result = await fn();
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
this.isProcessing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空队列
|
||||
*/
|
||||
clear() {
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停队列
|
||||
*/
|
||||
pause() {
|
||||
this.isProcessing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复队列
|
||||
*/
|
||||
resume() {
|
||||
this.isProcessing = false;
|
||||
this._process();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列长度
|
||||
* @returns {number} 队列长度
|
||||
*/
|
||||
get length() {
|
||||
return this.queue.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否正在处理
|
||||
* @returns {boolean} 是否正在处理
|
||||
*/
|
||||
get processing() {
|
||||
return this.isProcessing;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建动画队列
|
||||
* @returns {AnimationQueue} 动画队列实例
|
||||
*/
|
||||
export const createAnimationQueue = () => {
|
||||
return new AnimationQueue();
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画性能监控
|
||||
*/
|
||||
export class AnimationPerformanceMonitor {
|
||||
constructor() {
|
||||
this.metrics = {
|
||||
totalAnimations: 0,
|
||||
activeAnimations: 0,
|
||||
averageDuration: 0,
|
||||
maxDuration: 0,
|
||||
minDuration: Infinity,
|
||||
};
|
||||
|
||||
this.history = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录动画开始
|
||||
* @param {string} animationName - 动画名称
|
||||
*/
|
||||
recordStart(animationName) {
|
||||
this.metrics.totalAnimations++;
|
||||
this.metrics.activeAnimations++;
|
||||
|
||||
this.history.push({
|
||||
name: animationName,
|
||||
startTime: performance.now(),
|
||||
endTime: null,
|
||||
duration: null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录动画结束
|
||||
* @param {string} animationName - 动画名称
|
||||
*/
|
||||
recordEnd(animationName) {
|
||||
this.metrics.activeAnimations--;
|
||||
|
||||
const entry = this.history.find(
|
||||
(h) => h.name === animationName && h.endTime === null
|
||||
);
|
||||
|
||||
if (entry) {
|
||||
entry.endTime = performance.now();
|
||||
entry.duration = entry.endTime - entry.startTime;
|
||||
|
||||
// 更新统计
|
||||
this.metrics.maxDuration = Math.max(this.metrics.maxDuration, entry.duration);
|
||||
this.metrics.minDuration = Math.min(this.metrics.minDuration, entry.duration);
|
||||
|
||||
// 计算平均值
|
||||
const completed = this.history.filter((h) => h.duration !== null);
|
||||
this.metrics.averageDuration =
|
||||
completed.reduce((sum, h) => sum + h.duration, 0) / completed.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指标
|
||||
* @returns {Object} 性能指标
|
||||
*/
|
||||
getMetrics() {
|
||||
return { ...this.metrics };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
* @returns {Array} 历史记录
|
||||
*/
|
||||
getHistory() {
|
||||
return [...this.history];
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置监控
|
||||
*/
|
||||
reset() {
|
||||
this.metrics = {
|
||||
totalAnimations: 0,
|
||||
activeAnimations: 0,
|
||||
averageDuration: 0,
|
||||
maxDuration: 0,
|
||||
minDuration: Infinity,
|
||||
};
|
||||
|
||||
this.history = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建性能监控实例
|
||||
* @returns {AnimationPerformanceMonitor} 性能监控实例
|
||||
*/
|
||||
export const createPerformanceMonitor = () => {
|
||||
return new AnimationPerformanceMonitor();
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画缓存管理
|
||||
*/
|
||||
export const animationCacheManager = {
|
||||
/**
|
||||
* 缓存动画
|
||||
* @param {string} key - 缓存键
|
||||
* @param {Object} animation - 动画配置
|
||||
*/
|
||||
set(key, animation) {
|
||||
animationCache.set(key, animation);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取缓存动画
|
||||
* @param {string} key - 缓存键
|
||||
* @returns {Object|null} 动画配置
|
||||
*/
|
||||
get(key) {
|
||||
return animationCache.get(key) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查缓存
|
||||
* @param {string} key - 缓存键
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
has(key) {
|
||||
return animationCache.has(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param {string} key - 缓存键
|
||||
*/
|
||||
delete(key) {
|
||||
animationCache.delete(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
*/
|
||||
clear() {
|
||||
animationCache.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取缓存大小
|
||||
* @returns {number} 缓存大小
|
||||
*/
|
||||
size() {
|
||||
return animationCache.size;
|
||||
},
|
||||
};
|
||||
|
||||
export { AnimationManager, defaultAnimationManager };
|
||||
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* MetonaToast Constants - 常量定义
|
||||
* @module constants
|
||||
* @version 2.0.0
|
||||
* @description 默认配置、颜色、动画、主题等常量
|
||||
*/
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
export const DEFAULTS = Object.freeze({
|
||||
// 基础配置
|
||||
position: 'top-right',
|
||||
duration: 4000,
|
||||
max: 6,
|
||||
gap: 12,
|
||||
offset: 24,
|
||||
|
||||
// 交互配置
|
||||
pauseOnHover: true,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
|
||||
// 显示配置
|
||||
showProgress: true,
|
||||
progressDirection: 'horizontal',
|
||||
icon: true,
|
||||
closeButton: true,
|
||||
|
||||
// 主题配置
|
||||
theme: 'auto',
|
||||
animation: 'slide',
|
||||
|
||||
// 布局配置
|
||||
zIndex: 9999,
|
||||
width: 360,
|
||||
|
||||
// 自定义配置
|
||||
className: '',
|
||||
style: {},
|
||||
|
||||
// 回调函数
|
||||
onShow: null,
|
||||
onClose: null,
|
||||
onClick: null,
|
||||
|
||||
// 国际化
|
||||
locale: 'zh-CN',
|
||||
|
||||
// 插件
|
||||
plugins: [],
|
||||
});
|
||||
|
||||
// Icons — 从 icons.js 导入并重新导出(保持向后兼容)
|
||||
import { ICONS } from './icons.js';
|
||||
export { ICONS };
|
||||
|
||||
/**
|
||||
* 类型颜色配置
|
||||
*/
|
||||
export const TYPE_COLORS = {
|
||||
success: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
error: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
warning: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
info: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
loading: { fg: '#6366f1', bg: 'rgba(99, 102, 241, 0.1)' },
|
||||
default: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
check: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
x: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
alert: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
question: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
star: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
heart: { fg: '#ec4899', bg: 'rgba(236, 72, 153, 0.1)' },
|
||||
bell: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
mail: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
settings: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
user: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
home: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
search: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
plus: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
minus: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
edit: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
trash: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
download: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
upload: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
share: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
link: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
external: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
clock: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
calendar: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
map: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
compass: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
globe: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
wifi: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
cloud: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
sun: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
moon: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
zap: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
activity: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
cpu: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
database: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
server: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
terminal: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
code: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
git: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
package: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
layers: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
grid: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
list: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
filter: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
sort: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
refresh: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
sync: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
power: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
battery: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
bluetooth: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
volume: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
mic: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
camera: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
image: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
video: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
music: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
file: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
folder: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
clipboard: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
save: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
print: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
eye: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
eyeOff: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
lock: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
unlock: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
shield: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
key: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
flag: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
bookmark: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
tag: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
gift: { fg: '#ec4899', bg: 'rgba(236, 72, 153, 0.1)' },
|
||||
award: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
target: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
crosshair: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
move: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
maximize: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
minimize: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
copy: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
cut: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
paste: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
rotateCw: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
rotateCcw: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
zoomIn: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
zoomOut: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
crop: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
sliders: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
toggleLeft: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
toggleRight: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
checkCircle: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
xCircle: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
alertCircle: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
infoCircle: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
helpCircle: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
alertTriangle: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
checkSquare: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
|
||||
square: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
circle: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
|
||||
triangle: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
|
||||
hexagon: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
|
||||
octagon: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
|
||||
pentagon: { fg: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.1)' },
|
||||
diamond: { fg: '#ec4899', bg: 'rgba(236, 72, 153, 0.1)' },
|
||||
};
|
||||
|
||||
/**
|
||||
* 动画配置
|
||||
*/
|
||||
export const ANIMATIONS = {
|
||||
slide: {
|
||||
enter: { transform: 'translateX(80px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
fade: {
|
||||
enter: { opacity: 0, filter: 'blur(3px)' },
|
||||
leave: { opacity: 0, filter: 'blur(3px)' },
|
||||
duration: 500,
|
||||
easing: 'ease',
|
||||
},
|
||||
scale: {
|
||||
enter: { transform: 'scale(0.55)', opacity: 0 },
|
||||
leave: { transform: 'scale(0.55)', opacity: 0 },
|
||||
duration: 450,
|
||||
easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||
},
|
||||
bounce: {
|
||||
enter: { transform: 'translateY(-80px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(20px)', opacity: 0 },
|
||||
duration: 650,
|
||||
easing: 'ease',
|
||||
},
|
||||
flip: {
|
||||
enter: { transform: 'perspective(500px) rotateX(-90deg)', opacity: 0 },
|
||||
leave: { transform: 'perspective(500px) rotateX(90deg)', opacity: 0 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
rotate: {
|
||||
enter: { transform: 'rotate(-25deg) scale(0.6)', opacity: 0 },
|
||||
leave: { transform: 'rotate(25deg) scale(0.6)', opacity: 0 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
zoom: {
|
||||
enter: { transform: 'scale(0.1)', opacity: 0 },
|
||||
leave: { transform: 'scale(0.1)', opacity: 0 },
|
||||
duration: 500,
|
||||
easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||
},
|
||||
slideUp: {
|
||||
enter: { transform: 'translateY(60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(-120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideDown: {
|
||||
enter: { transform: 'translateY(-60px)', opacity: 0 },
|
||||
leave: { transform: 'translateY(120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideLeft: {
|
||||
enter: { transform: 'translateX(-90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
slideRight: {
|
||||
enter: { transform: 'translateX(90px)', opacity: 0 },
|
||||
leave: { transform: 'translateX(-120%)', opacity: 0 },
|
||||
duration: 400,
|
||||
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 主题配置
|
||||
*/
|
||||
export const THEMES = {
|
||||
light: {
|
||||
bg: 'rgba(255, 255, 255, 0.96)',
|
||||
text: '#1f2937',
|
||||
border: 'rgba(0, 0, 0, 0.06)',
|
||||
shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.18), 0 4px 14px -4px rgba(0, 0, 0, 0.08)',
|
||||
hoverShadow: '0 14px 48px -10px rgba(0, 0, 0, 0.22), 0 6px 18px -4px rgba(0, 0, 0, 0.10)',
|
||||
progressBg: 'rgba(0, 0, 0, 0.06)',
|
||||
closeHoverBg: 'rgba(0, 0, 0, 0.06)',
|
||||
},
|
||||
dark: {
|
||||
bg: 'rgba(28, 32, 40, 0.94)',
|
||||
text: '#e6e8eb',
|
||||
border: 'rgba(255, 255, 255, 0.08)',
|
||||
shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.6), 0 4px 14px -4px rgba(0, 0, 0, 0.4)',
|
||||
hoverShadow: '0 14px 48px -8px rgba(0, 0, 0, 0.6), 0 6px 18px -4px rgba(0, 0, 0, 0.4)',
|
||||
progressBg: 'rgba(255, 255, 255, 0.08)',
|
||||
closeHoverBg: 'rgba(255, 255, 255, 0.08)',
|
||||
},
|
||||
auto: 'auto',
|
||||
warm: {
|
||||
bg: 'rgba(255, 251, 235, 0.96)',
|
||||
text: '#78350f',
|
||||
border: 'rgba(245, 158, 11, 0.2)',
|
||||
shadow: '0 10px 36px -10px rgba(245, 158, 11, 0.18), 0 4px 14px -4px rgba(245, 158, 11, 0.08)',
|
||||
hoverShadow: '0 14px 48px -10px rgba(245, 158, 11, 0.22), 0 6px 18px -4px rgba(245, 158, 11, 0.10)',
|
||||
progressBg: 'rgba(245, 158, 11, 0.1)',
|
||||
closeHoverBg: 'rgba(245, 158, 11, 0.1)',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 位置配置
|
||||
*/
|
||||
export const POSITIONS = [
|
||||
'top-left',
|
||||
'top-center',
|
||||
'top-right',
|
||||
'bottom-left',
|
||||
'bottom-center',
|
||||
'bottom-right',
|
||||
];
|
||||
|
||||
/**
|
||||
* 类型配置
|
||||
*/
|
||||
export const TYPES = [
|
||||
'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',
|
||||
];
|
||||
|
||||
// Locales — 从 locales.js 导入并重新导出(保持向后兼容)
|
||||
import { LOCALES } from './locales.js';
|
||||
export { LOCALES };
|
||||
|
||||
/**
|
||||
* 进度条方向
|
||||
*/
|
||||
export const PROGRESS_DIRECTIONS = ['horizontal', 'vertical'];
|
||||
|
||||
/**
|
||||
* 动画类型
|
||||
*/
|
||||
export const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', 'zoom', 'slideUp', 'slideDown', 'slideLeft', 'slideRight'];
|
||||
|
||||
/**
|
||||
* 主题类型
|
||||
*/
|
||||
export const THEME_TYPES = ['light', 'dark', 'auto'];
|
||||
+1070
File diff suppressed because it is too large
Load Diff
+1054
File diff suppressed because it is too large
Load Diff
+603
@@ -0,0 +1,603 @@
|
||||
/**
|
||||
* MetonaToast Icons — 图标SVG定义
|
||||
* @module icons
|
||||
* @version 2.0.0
|
||||
* @description 80+ 内置SVG图标
|
||||
*/
|
||||
|
||||
export const ICONS = {
|
||||
success: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>`,
|
||||
|
||||
error: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="15" y1="9" x2="9" y2="15"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
warning: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
info: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||
</svg>`,
|
||||
|
||||
loading: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56" class="met-spin"/>
|
||||
</svg>`,
|
||||
|
||||
close: `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
check: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>`,
|
||||
|
||||
x: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
alert: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
question: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
star: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||||
</svg>`,
|
||||
|
||||
heart: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
|
||||
</svg>`,
|
||||
|
||||
bell: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
||||
</svg>`,
|
||||
|
||||
mail: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>`,
|
||||
|
||||
settings: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
</svg>`,
|
||||
|
||||
user: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>`,
|
||||
|
||||
home: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
<polyline points="9 22 9 12 15 12 15 22"/>
|
||||
</svg>`,
|
||||
|
||||
search: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
</svg>`,
|
||||
|
||||
plus: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
minus: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
edit: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||||
</svg>`,
|
||||
|
||||
trash: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
download: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" y1="15" x2="12" y2="3"/>
|
||||
</svg>`,
|
||||
|
||||
upload: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="17 8 12 3 7 8"/>
|
||||
<line x1="12" y1="3" x2="12" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
share: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="18" cy="5" r="3"/>
|
||||
<circle cx="6" cy="12" r="3"/>
|
||||
<circle cx="18" cy="19" r="3"/>
|
||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
|
||||
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
||||
</svg>`,
|
||||
|
||||
link: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||
</svg>`,
|
||||
|
||||
external: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" y1="14" x2="21" y2="3"/>
|
||||
</svg>`,
|
||||
|
||||
clock: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>`,
|
||||
|
||||
calendar: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="16" y1="2" x2="16" y2="6"/>
|
||||
<line x1="8" y1="2" x2="8" y2="6"/>
|
||||
<line x1="3" y1="10" x2="21" y2="10"/>
|
||||
</svg>`,
|
||||
|
||||
map: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/>
|
||||
<line x1="8" y1="2" x2="8" y2="18"/>
|
||||
<line x1="16" y1="6" x2="16" y2="22"/>
|
||||
</svg>`,
|
||||
|
||||
compass: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/>
|
||||
</svg>`,
|
||||
|
||||
globe: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="2" y1="12" x2="22" y2="12"/>
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
|
||||
</svg>`,
|
||||
|
||||
wifi: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
|
||||
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
|
||||
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
|
||||
<line x1="12" y1="20" x2="12.01" y2="20"/>
|
||||
</svg>`,
|
||||
|
||||
cloud: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/>
|
||||
</svg>`,
|
||||
|
||||
sun: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"/>
|
||||
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||
<line x1="12" y1="21" x2="12" y2="23"/>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||
<line x1="1" y1="12" x2="3" y2="12"/>
|
||||
<line x1="21" y1="12" x2="23" y2="12"/>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||
</svg>`,
|
||||
|
||||
moon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||
</svg>`,
|
||||
|
||||
zap: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
||||
</svg>`,
|
||||
|
||||
activity: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
|
||||
</svg>`,
|
||||
|
||||
cpu: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"/>
|
||||
<rect x="9" y="9" width="6" height="6"/>
|
||||
<line x1="9" y1="1" x2="9" y2="4"/>
|
||||
<line x1="15" y1="1" x2="15" y2="4"/>
|
||||
<line x1="9" y1="20" x2="9" y2="23"/>
|
||||
<line x1="15" y1="20" x2="15" y2="23"/>
|
||||
<line x1="20" y1="9" x2="23" y2="9"/>
|
||||
<line x1="20" y1="14" x2="23" y2="14"/>
|
||||
<line x1="1" y1="9" x2="4" y2="9"/>
|
||||
<line x1="1" y1="14" x2="4" y2="14"/>
|
||||
</svg>`,
|
||||
|
||||
database: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<ellipse cx="12" cy="5" rx="9" ry="3"/>
|
||||
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
|
||||
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
||||
</svg>`,
|
||||
|
||||
server: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/>
|
||||
<rect x="2" y="14" width="20" height="8" rx="2" ry="2"/>
|
||||
<line x1="6" y1="6" x2="6.01" y2="6"/>
|
||||
<line x1="6" y1="18" x2="6.01" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
terminal: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="4 17 10 11 4 5"/>
|
||||
<line x1="12" y1="19" x2="20" y2="19"/>
|
||||
</svg>`,
|
||||
|
||||
code: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="16 18 22 12 16 6"/>
|
||||
<polyline points="8 6 2 12 8 18"/>
|
||||
</svg>`,
|
||||
|
||||
git: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="18" cy="18" r="3"/>
|
||||
<circle cx="6" cy="6" r="3"/>
|
||||
<path d="M13 6h3a2 2 0 0 1 2 2v7"/>
|
||||
<line x1="6" y1="9" x2="6" y2="21"/>
|
||||
</svg>`,
|
||||
|
||||
package: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"/>
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
layers: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 2 7 12 12 22 7 12 2"/>
|
||||
<polyline points="2 17 12 22 22 17"/>
|
||||
<polyline points="2 12 12 17 22 12"/>
|
||||
</svg>`,
|
||||
|
||||
grid: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/>
|
||||
<rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>`,
|
||||
|
||||
list: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="8" y1="6" x2="21" y2="6"/>
|
||||
<line x1="8" y1="12" x2="21" y2="12"/>
|
||||
<line x1="8" y1="18" x2="21" y2="18"/>
|
||||
<line x1="3" y1="6" x2="3.01" y2="6"/>
|
||||
<line x1="3" y1="12" x2="3.01" y2="12"/>
|
||||
<line x1="3" y1="18" x2="3.01" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
filter: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
|
||||
</svg>`,
|
||||
|
||||
sort: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<polyline points="19 12 12 19 5 12"/>
|
||||
</svg>`,
|
||||
|
||||
refresh: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>`,
|
||||
|
||||
sync: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>`,
|
||||
|
||||
power: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>
|
||||
<line x1="12" y1="2" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
battery: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="6" width="18" height="12" rx="2" ry="2"/>
|
||||
<line x1="23" y1="13" x2="23" y2="11"/>
|
||||
</svg>`,
|
||||
|
||||
bluetooth: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6.5 6.5 17.5 17.5 12 23 12 1 17.5 6.5 6.5 17.5"/>
|
||||
</svg>`,
|
||||
|
||||
volume: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
<path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/>
|
||||
</svg>`,
|
||||
|
||||
mic: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
||||
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
||||
<line x1="12" y1="19" x2="12" y2="23"/>
|
||||
<line x1="8" y1="23" x2="16" y2="23"/>
|
||||
</svg>`,
|
||||
|
||||
camera: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
|
||||
<circle cx="12" cy="13" r="4"/>
|
||||
</svg>`,
|
||||
|
||||
image: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||||
<polyline points="21 15 16 10 5 21"/>
|
||||
</svg>`,
|
||||
|
||||
video: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="23 7 16 12 23 17 23 7"/>
|
||||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||||
</svg>`,
|
||||
|
||||
music: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M9 18V5l12-2v13"/>
|
||||
<circle cx="6" cy="18" r="3"/>
|
||||
<circle cx="18" cy="16" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
file: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
|
||||
<polyline points="13 2 13 9 20 9"/>
|
||||
</svg>`,
|
||||
|
||||
folder: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
|
||||
</svg>`,
|
||||
|
||||
clipboard: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
|
||||
</svg>`,
|
||||
|
||||
save: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
||||
<polyline points="17 21 17 13 7 13 7 21"/>
|
||||
<polyline points="7 3 7 8 15 8"/>
|
||||
</svg>`,
|
||||
|
||||
print: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 6 2 18 2 18 9"/>
|
||||
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/>
|
||||
<rect x="6" y="14" width="12" height="8"/>
|
||||
</svg>`,
|
||||
|
||||
eye: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
eyeOff: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/>
|
||||
<line x1="1" y1="1" x2="23" y2="23"/>
|
||||
</svg>`,
|
||||
|
||||
lock: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
</svg>`,
|
||||
|
||||
unlock: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 9.9-1"/>
|
||||
</svg>`,
|
||||
|
||||
shield: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>`,
|
||||
|
||||
key: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/>
|
||||
</svg>`,
|
||||
|
||||
flag: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/>
|
||||
<line x1="4" y1="22" x2="4" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
bookmark: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/>
|
||||
</svg>`,
|
||||
|
||||
tag: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/>
|
||||
<line x1="7" y1="7" x2="7.01" y2="7"/>
|
||||
</svg>`,
|
||||
|
||||
gift: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 12 20 22 4 22 4 12"/>
|
||||
<rect x="2" y="7" width="20" height="5"/>
|
||||
<line x1="12" y1="22" x2="12" y2="7"/>
|
||||
<path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"/>
|
||||
<path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"/>
|
||||
</svg>`,
|
||||
|
||||
award: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="8" r="7"/>
|
||||
<polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"/>
|
||||
</svg>`,
|
||||
|
||||
target: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<circle cx="12" cy="12" r="6"/>
|
||||
<circle cx="12" cy="12" r="2"/>
|
||||
</svg>`,
|
||||
|
||||
crosshair: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="22" y1="12" x2="18" y2="12"/>
|
||||
<line x1="6" y1="12" x2="2" y2="12"/>
|
||||
<line x1="12" y1="6" x2="12" y2="2"/>
|
||||
<line x1="12" y1="22" x2="12" y2="18"/>
|
||||
</svg>`,
|
||||
|
||||
move: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="5 9 2 12 5 15"/>
|
||||
<polyline points="9 5 12 2 15 5"/>
|
||||
<polyline points="15 19 12 22 9 19"/>
|
||||
<polyline points="19 9 22 12 19 15"/>
|
||||
<line x1="2" y1="12" x2="22" y2="12"/>
|
||||
<line x1="12" y1="2" x2="12" y2="22"/>
|
||||
</svg>`,
|
||||
|
||||
maximize: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
||||
</svg>`,
|
||||
|
||||
minimize: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
|
||||
</svg>`,
|
||||
|
||||
copy: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg>`,
|
||||
|
||||
cut: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="6" cy="6" r="3"/>
|
||||
<circle cx="6" cy="18" r="3"/>
|
||||
<line x1="20" y1="4" x2="8.12" y2="15.88"/>
|
||||
<line x1="14.47" y1="14.48" x2="20" y2="20"/>
|
||||
<line x1="8.12" y1="8.12" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
paste: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
|
||||
</svg>`,
|
||||
|
||||
rotateCw: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||||
</svg>`,
|
||||
|
||||
rotateCcw: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>`,
|
||||
|
||||
zoomIn: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
<line x1="11" y1="8" x2="11" y2="14"/>
|
||||
<line x1="8" y1="11" x2="14" y2="11"/>
|
||||
</svg>`,
|
||||
|
||||
zoomOut: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
<line x1="8" y1="11" x2="14" y2="11"/>
|
||||
</svg>`,
|
||||
|
||||
crop: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6.13 1L6 16a2 2 0 0 0 2 2h15"/>
|
||||
<path d="M1 6.13L16 6a2 2 0 0 1 2 2v15"/>
|
||||
</svg>`,
|
||||
|
||||
sliders: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="4" y1="21" x2="4" y2="14"/>
|
||||
<line x1="4" y1="10" x2="4" y2="3"/>
|
||||
<line x1="12" y1="21" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12" y2="3"/>
|
||||
<line x1="20" y1="21" x2="20" y2="16"/>
|
||||
<line x1="20" y1="12" x2="20" y2="3"/>
|
||||
<line x1="1" y1="14" x2="7" y2="14"/>
|
||||
<line x1="9" y1="8" x2="15" y2="8"/>
|
||||
<line x1="17" y1="16" x2="23" y2="16"/>
|
||||
</svg>`,
|
||||
|
||||
toggleLeft: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="5" width="22" height="14" rx="7" ry="7"/>
|
||||
<circle cx="8" cy="12" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
toggleRight: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="5" width="22" height="14" rx="7" ry="7"/>
|
||||
<circle cx="16" cy="12" r="3"/>
|
||||
</svg>`,
|
||||
|
||||
checkCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
||||
<polyline points="22 4 12 14.01 9 11.01"/>
|
||||
</svg>`,
|
||||
|
||||
xCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="15" y1="9" x2="9" y2="15"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15"/>
|
||||
</svg>`,
|
||||
|
||||
alertCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="8" x2="12" y2="12"/>
|
||||
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||
</svg>`,
|
||||
|
||||
infoCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||
</svg>`,
|
||||
|
||||
helpCircle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
alertTriangle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>`,
|
||||
|
||||
checkSquare: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 11 12 14 22 4"/>
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
||||
</svg>`,
|
||||
|
||||
square: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
</svg>`,
|
||||
|
||||
circle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
</svg>`,
|
||||
|
||||
triangle: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
</svg>`,
|
||||
|
||||
hexagon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
</svg>`,
|
||||
|
||||
octagon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"/>
|
||||
</svg>`,
|
||||
|
||||
pentagon: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 22 8.5 19 20 5 20 2 8.5 12 2"/>
|
||||
</svg>`,
|
||||
|
||||
diamond: `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4.5" y="4.5" width="15" height="15" rx="1" transform="rotate(45 12 12)"/>
|
||||
</svg>`,
|
||||
};
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* MetonaToast - 轻量级Toast通知库
|
||||
* @module metona-toast
|
||||
* @version 2.0.0
|
||||
* @author metona
|
||||
* @description 轻量、零依赖、精致美观的Toast通知库。单文件,开箱即用。
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { meToast, Toast } from './core.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';
|
||||
|
||||
// 版本信息
|
||||
const VERSION = '2.0.0';
|
||||
|
||||
/**
|
||||
* 主对象增强
|
||||
*/
|
||||
const enhancedMeToast = {
|
||||
...meToast,
|
||||
|
||||
version: VERSION,
|
||||
|
||||
animations: animationUtils,
|
||||
themes: themeUtils,
|
||||
i18n: i18nUtils,
|
||||
plugins: pluginUtils,
|
||||
presetPlugins,
|
||||
|
||||
/**
|
||||
* 安装插件,并连接必要的生命周期钩子
|
||||
*/
|
||||
use(plugin, options = {}) {
|
||||
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) => preset.announce(toast));
|
||||
}
|
||||
if (plugin === 'persistence') {
|
||||
const saved = preset.install();
|
||||
if (saved) this.configure(saved);
|
||||
Toast.on('afterClose', () => { preset.save(this.getConfig()); });
|
||||
}
|
||||
} else if (plugin && typeof plugin === 'object') {
|
||||
const name = plugin.name || 'custom';
|
||||
this.plugins.register(name, { ...plugin, ...options });
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init(options = {}) {
|
||||
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((plugin) => {
|
||||
this.use(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
destroy() {
|
||||
this.dismiss();
|
||||
|
||||
if (this.plugins && typeof this.plugins.destroy === 'function') {
|
||||
this.plugins.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) => c.parentNode && c.parentNode.removeChild(c));
|
||||
|
||||
const style = document.getElementById('metona-toast-styles');
|
||||
if (style) style.parentNode.removeChild(style);
|
||||
}
|
||||
|
||||
this._toasts.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取状态
|
||||
*/
|
||||
getStatus() {
|
||||
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() {
|
||||
return { ...this._config };
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
*/
|
||||
updateConfig(config) {
|
||||
if (config && typeof config === 'object') {
|
||||
this._config = { ...this._config, ...config };
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置配置
|
||||
*/
|
||||
resetConfig() {
|
||||
this._config = { ...DEFAULTS };
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取Toast列表
|
||||
*/
|
||||
getToasts() {
|
||||
return Array.from(this._toasts.values());
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查是否有Toast
|
||||
*/
|
||||
hasToasts() {
|
||||
return this._toasts.size > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取Toast
|
||||
*/
|
||||
getToast(id) {
|
||||
if (!id) return null;
|
||||
return this._toasts.get(id) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭所有Toast
|
||||
*/
|
||||
closeAll() {
|
||||
this._toasts.forEach((t) => t.close());
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除所有Toast
|
||||
*/
|
||||
clearAll() {
|
||||
this._toasts.forEach((t) => t.close());
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂停所有Toast
|
||||
*/
|
||||
pauseAll() {
|
||||
this._toasts.forEach((t) => t._pause());
|
||||
},
|
||||
|
||||
/**
|
||||
* 恢复所有Toast
|
||||
*/
|
||||
resumeAll() {
|
||||
this._toasts.forEach((t) => t._resume());
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新所有Toast
|
||||
*/
|
||||
updateAll(partial) {
|
||||
if (partial && typeof partial === 'object') {
|
||||
this._toasts.forEach((t) => t.update(partial));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 查找Toast
|
||||
*/
|
||||
findToasts(predicate) {
|
||||
if (typeof predicate !== 'function') return [];
|
||||
return Array.from(this._toasts.values()).filter(predicate);
|
||||
},
|
||||
|
||||
/**
|
||||
* 按类型查找Toast
|
||||
*/
|
||||
findByType(type) {
|
||||
return this.findToasts((t) => t.type === type);
|
||||
},
|
||||
|
||||
/**
|
||||
* 按位置查找Toast
|
||||
*/
|
||||
findByPosition(position) {
|
||||
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;
|
||||
window.metonaToast = enhancedMeToast;
|
||||
}
|
||||
|
||||
// 导出
|
||||
export default enhancedMeToast;
|
||||
export { enhancedMeToast as meToast, enhancedMeToast as Met, enhancedMeToast as MeToast, Toast, VERSION };
|
||||
+752
@@ -0,0 +1,752 @@
|
||||
/**
|
||||
* MetonaToast Locales — 国际化翻译数据
|
||||
* @module locales
|
||||
* @version 2.0.0
|
||||
* @description 内置 zh-CN / en-US 完整翻译
|
||||
*/
|
||||
|
||||
export const LOCALES = {
|
||||
'zh-CN': {
|
||||
close: '关闭',
|
||||
loading: '加载中...',
|
||||
success: '操作成功',
|
||||
error: '操作失败',
|
||||
warning: '警告',
|
||||
info: '信息',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
yes: '是',
|
||||
no: '否',
|
||||
ok: '确定',
|
||||
retry: '重试',
|
||||
undo: '撤销',
|
||||
redo: '重做',
|
||||
save: '保存',
|
||||
delete: '删除',
|
||||
edit: '编辑',
|
||||
add: '添加',
|
||||
remove: '移除',
|
||||
search: '搜索',
|
||||
filter: '筛选',
|
||||
sort: '排序',
|
||||
refresh: '刷新',
|
||||
sync: '同步',
|
||||
upload: '上传',
|
||||
download: '下载',
|
||||
share: '分享',
|
||||
copy: '复制',
|
||||
cut: '剪切',
|
||||
paste: '粘贴',
|
||||
print: '打印',
|
||||
export: '导出',
|
||||
import: '导入',
|
||||
settings: '设置',
|
||||
help: '帮助',
|
||||
about: '关于',
|
||||
version: '版本',
|
||||
language: '语言',
|
||||
theme: '主题',
|
||||
dark: '暗色',
|
||||
light: '亮色',
|
||||
auto: '自动',
|
||||
notifications: '通知',
|
||||
enable: '启用',
|
||||
disable: '禁用',
|
||||
on: '开启',
|
||||
off: '关闭',
|
||||
max: '最大',
|
||||
min: '最小',
|
||||
expand: '展开',
|
||||
collapse: '折叠',
|
||||
fullscreen: '全屏',
|
||||
exitFullscreen: '退出全屏',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '缩小',
|
||||
reset: '重置',
|
||||
apply: '应用',
|
||||
submit: '提交',
|
||||
send: '发送',
|
||||
receive: '接收',
|
||||
connect: '连接',
|
||||
disconnect: '断开',
|
||||
start: '开始',
|
||||
stop: '停止',
|
||||
pause: '暂停',
|
||||
resume: '继续',
|
||||
next: '下一个',
|
||||
previous: '上一个',
|
||||
first: '第一个',
|
||||
last: '最后一个',
|
||||
back: '返回',
|
||||
forward: '前进',
|
||||
home: '首页',
|
||||
menu: '菜单',
|
||||
more: '更多',
|
||||
less: '更少',
|
||||
show: '显示',
|
||||
hide: '隐藏',
|
||||
visible: '可见',
|
||||
hidden: '隐藏',
|
||||
enabled: '已启用',
|
||||
disabled: '已禁用',
|
||||
active: '活跃',
|
||||
inactive: '非活跃',
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
connected: '已连接',
|
||||
disconnected: '已断开',
|
||||
loading: '加载中',
|
||||
loaded: '已加载',
|
||||
saving: '保存中',
|
||||
saved: '已保存',
|
||||
deleting: '删除中',
|
||||
deleted: '已删除',
|
||||
uploading: '上传中',
|
||||
uploaded: '已上传',
|
||||
downloading: '下载中',
|
||||
downloaded: '已下载',
|
||||
processing: '处理中',
|
||||
processed: '已处理',
|
||||
syncing: '同步中',
|
||||
synced: '已同步',
|
||||
sending: '发送中',
|
||||
sent: '已发送',
|
||||
receiving: '接收中',
|
||||
received: '已接收',
|
||||
connecting: '连接中',
|
||||
error: '错误',
|
||||
warning: '警告',
|
||||
info: '信息',
|
||||
success: '成功',
|
||||
question: '问题',
|
||||
help: '帮助',
|
||||
feedback: '反馈',
|
||||
report: '报告',
|
||||
bug: '错误',
|
||||
feature: '功能',
|
||||
improvement: '改进',
|
||||
suggestion: '建议',
|
||||
request: '请求',
|
||||
response: '响应',
|
||||
status: '状态',
|
||||
progress: '进度',
|
||||
complete: '完成',
|
||||
incomplete: '未完成',
|
||||
pending: '待处理',
|
||||
processing: '处理中',
|
||||
failed: '失败',
|
||||
cancelled: '已取消',
|
||||
timeout: '超时',
|
||||
expired: '已过期',
|
||||
invalid: '无效',
|
||||
valid: '有效',
|
||||
required: '必填',
|
||||
optional: '可选',
|
||||
enabled: '启用',
|
||||
disabled: '禁用',
|
||||
allowed: '允许',
|
||||
denied: '拒绝',
|
||||
approved: '已批准',
|
||||
rejected: '已拒绝',
|
||||
accepted: '已接受',
|
||||
declined: '已拒绝',
|
||||
confirmed: '已确认',
|
||||
unconfirmed: '未确认',
|
||||
verified: '已验证',
|
||||
unverified: '未验证',
|
||||
authenticated: '已认证',
|
||||
unauthenticated: '未认证',
|
||||
authorized: '已授权',
|
||||
unauthorized: '未授权',
|
||||
public: '公开',
|
||||
private: '私有',
|
||||
protected: '受保护',
|
||||
internal: '内部',
|
||||
external: '外部',
|
||||
local: '本地',
|
||||
remote: '远程',
|
||||
global: '全局',
|
||||
system: '系统',
|
||||
user: '用户',
|
||||
admin: '管理员',
|
||||
moderator: '版主',
|
||||
guest: '访客',
|
||||
member: '成员',
|
||||
owner: '所有者',
|
||||
creator: '创建者',
|
||||
editor: '编辑者',
|
||||
viewer: '查看者',
|
||||
subscriber: '订阅者',
|
||||
follower: '关注者',
|
||||
following: '关注中',
|
||||
friend: '朋友',
|
||||
contact: '联系人',
|
||||
group: '群组',
|
||||
team: '团队',
|
||||
organization: '组织',
|
||||
company: '公司',
|
||||
department: '部门',
|
||||
project: '项目',
|
||||
task: '任务',
|
||||
issue: '问题',
|
||||
ticket: '工单',
|
||||
request: '请求',
|
||||
response: '响应',
|
||||
message: '消息',
|
||||
notification: '通知',
|
||||
alert: '警报',
|
||||
reminder: '提醒',
|
||||
event: '事件',
|
||||
activity: '活动',
|
||||
log: '日志',
|
||||
report: '报告',
|
||||
analytics: '分析',
|
||||
statistics: '统计',
|
||||
metrics: '指标',
|
||||
performance: '性能',
|
||||
security: '安全',
|
||||
privacy: '隐私',
|
||||
compliance: '合规',
|
||||
policy: '政策',
|
||||
terms: '条款',
|
||||
conditions: '条件',
|
||||
agreement: '协议',
|
||||
contract: '合同',
|
||||
license: '许可证',
|
||||
copyright: '版权',
|
||||
trademark: '商标',
|
||||
patent: '专利',
|
||||
tradeSecret: '商业秘密',
|
||||
confidential: '机密',
|
||||
sensitive: '敏感',
|
||||
critical: '关键',
|
||||
important: '重要',
|
||||
urgent: '紧急',
|
||||
high: '高',
|
||||
medium: '中',
|
||||
low: '低',
|
||||
none: '无',
|
||||
all: '全部',
|
||||
any: '任何',
|
||||
some: '一些',
|
||||
each: '每个',
|
||||
every: '每个',
|
||||
both: '两者',
|
||||
neither: '两者都不',
|
||||
either: '任一',
|
||||
other: '其他',
|
||||
another: '另一个',
|
||||
this: '这个',
|
||||
that: '那个',
|
||||
these: '这些',
|
||||
those: '那些',
|
||||
here: '这里',
|
||||
there: '那里',
|
||||
where: '哪里',
|
||||
when: '何时',
|
||||
why: '为什么',
|
||||
how: '如何',
|
||||
what: '什么',
|
||||
who: '谁',
|
||||
which: '哪个',
|
||||
whose: '谁的',
|
||||
whom: '谁',
|
||||
myself: '我自己',
|
||||
yourself: '你自己',
|
||||
himself: '他自己',
|
||||
herself: '她自己',
|
||||
itself: '它自己',
|
||||
ourselves: '我们自己',
|
||||
yourselves: '你们自己',
|
||||
themselves: '他们自己',
|
||||
mine: '我的',
|
||||
yours: '你的',
|
||||
his: '他的',
|
||||
hers: '她的',
|
||||
its: '它的',
|
||||
ours: '我们的',
|
||||
yours: '你们的',
|
||||
theirs: '他们的',
|
||||
me: '我',
|
||||
you: '你',
|
||||
him: '他',
|
||||
her: '她',
|
||||
it: '它',
|
||||
us: '我们',
|
||||
them: '他们',
|
||||
my: '我的',
|
||||
your: '你的',
|
||||
his: '他的',
|
||||
her: '她的',
|
||||
its: '它的',
|
||||
our: '我们的',
|
||||
your: '你们的',
|
||||
their: '他们的',
|
||||
i: '我',
|
||||
you: '你',
|
||||
he: '他',
|
||||
she: '她',
|
||||
it: '它',
|
||||
we: '我们',
|
||||
they: '他们',
|
||||
am: '是',
|
||||
is: '是',
|
||||
are: '是',
|
||||
was: '是',
|
||||
were: '是',
|
||||
be: '是',
|
||||
been: '是',
|
||||
being: '是',
|
||||
have: '有',
|
||||
has: '有',
|
||||
had: '有',
|
||||
having: '有',
|
||||
do: '做',
|
||||
does: '做',
|
||||
did: '做',
|
||||
doing: '做',
|
||||
will: '会',
|
||||
would: '会',
|
||||
shall: '将',
|
||||
should: '应该',
|
||||
may: '可以',
|
||||
might: '可能',
|
||||
can: '能',
|
||||
could: '能',
|
||||
must: '必须',
|
||||
need: '需要',
|
||||
dare: '敢',
|
||||
ought: '应该',
|
||||
used: '过去常常',
|
||||
to: '到',
|
||||
of: '的',
|
||||
in: '在',
|
||||
for: '为了',
|
||||
on: '在',
|
||||
with: '和',
|
||||
at: '在',
|
||||
by: '通过',
|
||||
from: '从',
|
||||
into: '进入',
|
||||
during: '在...期间',
|
||||
before: '在...之前',
|
||||
after: '在...之后',
|
||||
above: '在...上面',
|
||||
below: '在...下面',
|
||||
between: '在...之间',
|
||||
under: '在...下面',
|
||||
over: '在...上面',
|
||||
across: '穿过',
|
||||
through: '通过',
|
||||
into: '进入',
|
||||
towards: '朝向',
|
||||
upon: '在...上面',
|
||||
about: '关于',
|
||||
against: '反对',
|
||||
among: '在...之中',
|
||||
along: '沿着',
|
||||
around: '周围',
|
||||
beyond: '超出',
|
||||
but: '但是',
|
||||
despite: '尽管',
|
||||
except: '除了',
|
||||
inside: '里面',
|
||||
outside: '外面',
|
||||
since: '自从',
|
||||
until: '直到',
|
||||
unless: '除非',
|
||||
whether: '是否',
|
||||
while: '当...时候',
|
||||
although: '虽然',
|
||||
because: '因为',
|
||||
if: '如果',
|
||||
once: '一旦',
|
||||
since: '自从',
|
||||
so: '所以',
|
||||
that: '那个',
|
||||
though: '虽然',
|
||||
till: '直到',
|
||||
unless: '除非',
|
||||
until: '直到',
|
||||
when: '当...时候',
|
||||
whenever: '每当',
|
||||
where: '哪里',
|
||||
wherever: '无论哪里',
|
||||
whereas: '然而',
|
||||
wherever: '无论哪里',
|
||||
while: '当...时候',
|
||||
why: '为什么',
|
||||
},
|
||||
|
||||
'en-US': {
|
||||
close: 'Close',
|
||||
loading: 'Loading...',
|
||||
success: 'Success',
|
||||
error: 'Error',
|
||||
warning: 'Warning',
|
||||
info: 'Info',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
ok: 'OK',
|
||||
retry: 'Retry',
|
||||
undo: 'Undo',
|
||||
redo: 'Redo',
|
||||
save: 'Save',
|
||||
delete: 'Delete',
|
||||
edit: 'Edit',
|
||||
add: 'Add',
|
||||
remove: 'Remove',
|
||||
search: 'Search',
|
||||
filter: 'Filter',
|
||||
sort: 'Sort',
|
||||
refresh: 'Refresh',
|
||||
sync: 'Sync',
|
||||
upload: 'Upload',
|
||||
download: 'Download',
|
||||
share: 'Share',
|
||||
copy: 'Copy',
|
||||
cut: 'Cut',
|
||||
paste: 'Paste',
|
||||
print: 'Print',
|
||||
export: 'Export',
|
||||
import: 'Import',
|
||||
settings: 'Settings',
|
||||
help: 'Help',
|
||||
about: 'About',
|
||||
version: 'Version',
|
||||
language: 'Language',
|
||||
theme: 'Theme',
|
||||
dark: 'Dark',
|
||||
light: 'Light',
|
||||
auto: 'Auto',
|
||||
notifications: 'Notifications',
|
||||
enable: 'Enable',
|
||||
disable: 'Disable',
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
max: 'Max',
|
||||
min: 'Min',
|
||||
expand: 'Expand',
|
||||
collapse: 'Collapse',
|
||||
fullscreen: 'Fullscreen',
|
||||
exitFullscreen: 'Exit Fullscreen',
|
||||
zoomIn: 'Zoom In',
|
||||
zoomOut: 'Zoom Out',
|
||||
reset: 'Reset',
|
||||
apply: 'Apply',
|
||||
submit: 'Submit',
|
||||
send: 'Send',
|
||||
receive: 'Receive',
|
||||
connect: 'Connect',
|
||||
disconnect: 'Disconnect',
|
||||
start: 'Start',
|
||||
stop: 'Stop',
|
||||
pause: 'Pause',
|
||||
resume: 'Resume',
|
||||
next: 'Next',
|
||||
previous: 'Previous',
|
||||
first: 'First',
|
||||
last: 'Last',
|
||||
back: 'Back',
|
||||
forward: 'Forward',
|
||||
home: 'Home',
|
||||
menu: 'Menu',
|
||||
more: 'More',
|
||||
less: 'Less',
|
||||
show: 'Show',
|
||||
hide: 'Hide',
|
||||
visible: 'Visible',
|
||||
hidden: 'Hidden',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
active: 'Active',
|
||||
inactive: 'Inactive',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
connected: 'Connected',
|
||||
disconnected: 'Disconnected',
|
||||
loading: 'Loading',
|
||||
loaded: 'Loaded',
|
||||
saving: 'Saving',
|
||||
saved: 'Saved',
|
||||
deleting: 'Deleting',
|
||||
deleted: 'Deleted',
|
||||
uploading: 'Uploading',
|
||||
uploaded: 'Uploaded',
|
||||
downloading: 'Downloading',
|
||||
downloaded: 'Downloaded',
|
||||
processing: 'Processing',
|
||||
processed: 'Processed',
|
||||
syncing: 'Syncing',
|
||||
synced: 'Synced',
|
||||
sending: 'Sending',
|
||||
sent: 'Sent',
|
||||
receiving: 'Receiving',
|
||||
received: 'Received',
|
||||
connecting: 'Connecting',
|
||||
error: 'Error',
|
||||
warning: 'Warning',
|
||||
info: 'Info',
|
||||
success: 'Success',
|
||||
question: 'Question',
|
||||
help: 'Help',
|
||||
feedback: 'Feedback',
|
||||
report: 'Report',
|
||||
bug: 'Bug',
|
||||
feature: 'Feature',
|
||||
improvement: 'Improvement',
|
||||
suggestion: 'Suggestion',
|
||||
request: 'Request',
|
||||
response: 'Response',
|
||||
status: 'Status',
|
||||
progress: 'Progress',
|
||||
complete: 'Complete',
|
||||
incomplete: 'Incomplete',
|
||||
pending: 'Pending',
|
||||
processing: 'Processing',
|
||||
failed: 'Failed',
|
||||
cancelled: 'Cancelled',
|
||||
timeout: 'Timeout',
|
||||
expired: 'Expired',
|
||||
invalid: 'Invalid',
|
||||
valid: 'Valid',
|
||||
required: 'Required',
|
||||
optional: 'Optional',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
allowed: 'Allowed',
|
||||
denied: 'Denied',
|
||||
approved: 'Approved',
|
||||
rejected: 'Rejected',
|
||||
accepted: 'Accepted',
|
||||
declined: 'Declined',
|
||||
confirmed: 'Confirmed',
|
||||
unconfirmed: 'Unconfirmed',
|
||||
verified: 'Verified',
|
||||
unverified: 'Unverified',
|
||||
authenticated: 'Authenticated',
|
||||
unauthenticated: 'Unauthenticated',
|
||||
authorized: 'Authorized',
|
||||
unauthorized: 'Unauthorized',
|
||||
public: 'Public',
|
||||
private: 'Private',
|
||||
protected: 'Protected',
|
||||
internal: 'Internal',
|
||||
external: 'External',
|
||||
local: 'Local',
|
||||
remote: 'Remote',
|
||||
global: 'Global',
|
||||
system: 'System',
|
||||
user: 'User',
|
||||
admin: 'Admin',
|
||||
moderator: 'Moderator',
|
||||
guest: 'Guest',
|
||||
member: 'Member',
|
||||
owner: 'Owner',
|
||||
creator: 'Creator',
|
||||
editor: 'Editor',
|
||||
viewer: 'Viewer',
|
||||
subscriber: 'Subscriber',
|
||||
follower: 'Follower',
|
||||
following: 'Following',
|
||||
friend: 'Friend',
|
||||
contact: 'Contact',
|
||||
group: 'Group',
|
||||
team: 'Team',
|
||||
organization: 'Organization',
|
||||
company: 'Company',
|
||||
department: 'Department',
|
||||
project: 'Project',
|
||||
task: 'Task',
|
||||
issue: 'Issue',
|
||||
ticket: 'Ticket',
|
||||
request: 'Request',
|
||||
response: 'Response',
|
||||
message: 'Message',
|
||||
notification: 'Notification',
|
||||
alert: 'Alert',
|
||||
reminder: 'Reminder',
|
||||
event: 'Event',
|
||||
activity: 'Activity',
|
||||
log: 'Log',
|
||||
report: 'Report',
|
||||
analytics: 'Analytics',
|
||||
statistics: 'Statistics',
|
||||
metrics: 'Metrics',
|
||||
performance: 'Performance',
|
||||
security: 'Security',
|
||||
privacy: 'Privacy',
|
||||
compliance: 'Compliance',
|
||||
policy: 'Policy',
|
||||
terms: 'Terms',
|
||||
conditions: 'Conditions',
|
||||
agreement: 'Agreement',
|
||||
contract: 'Contract',
|
||||
license: 'License',
|
||||
copyright: 'Copyright',
|
||||
trademark: 'Trademark',
|
||||
patent: 'Patent',
|
||||
tradeSecret: 'Trade Secret',
|
||||
confidential: 'Confidential',
|
||||
sensitive: 'Sensitive',
|
||||
critical: 'Critical',
|
||||
important: 'Important',
|
||||
urgent: 'Urgent',
|
||||
high: 'High',
|
||||
medium: 'Medium',
|
||||
low: 'Low',
|
||||
none: 'None',
|
||||
all: 'All',
|
||||
any: 'Any',
|
||||
some: 'Some',
|
||||
each: 'Each',
|
||||
every: 'Every',
|
||||
both: 'Both',
|
||||
neither: 'Neither',
|
||||
either: 'Either',
|
||||
other: 'Other',
|
||||
another: 'Another',
|
||||
this: 'This',
|
||||
that: 'That',
|
||||
these: 'These',
|
||||
those: 'Those',
|
||||
here: 'Here',
|
||||
there: 'There',
|
||||
where: 'Where',
|
||||
when: 'When',
|
||||
why: 'Why',
|
||||
how: 'How',
|
||||
what: 'What',
|
||||
who: 'Who',
|
||||
which: 'Which',
|
||||
whose: 'Whose',
|
||||
whom: 'Whom',
|
||||
myself: 'Myself',
|
||||
yourself: 'Yourself',
|
||||
himself: 'Himself',
|
||||
herself: 'Herself',
|
||||
itself: 'Itself',
|
||||
ourselves: 'Ourselves',
|
||||
yourselves: 'Yourselves',
|
||||
themselves: 'Themselves',
|
||||
mine: 'Mine',
|
||||
yours: 'Yours',
|
||||
his: 'His',
|
||||
hers: 'Hers',
|
||||
its: 'Its',
|
||||
ours: 'Ours',
|
||||
yours: 'Yours',
|
||||
theirs: 'Theirs',
|
||||
me: 'Me',
|
||||
you: 'You',
|
||||
him: 'Him',
|
||||
her: 'Her',
|
||||
it: 'It',
|
||||
us: 'Us',
|
||||
them: 'Them',
|
||||
my: 'My',
|
||||
your: 'Your',
|
||||
his: 'His',
|
||||
her: 'Her',
|
||||
its: 'Its',
|
||||
our: 'Our',
|
||||
your: 'Your',
|
||||
their: 'Their',
|
||||
i: 'I',
|
||||
you: 'You',
|
||||
he: 'He',
|
||||
she: 'She',
|
||||
it: 'It',
|
||||
we: 'We',
|
||||
they: 'They',
|
||||
am: 'Am',
|
||||
is: 'Is',
|
||||
are: 'Are',
|
||||
was: 'Was',
|
||||
were: 'Were',
|
||||
be: 'Be',
|
||||
been: 'Been',
|
||||
being: 'Being',
|
||||
have: 'Have',
|
||||
has: 'Has',
|
||||
had: 'Had',
|
||||
having: 'Having',
|
||||
do: 'Do',
|
||||
does: 'Does',
|
||||
did: 'Did',
|
||||
doing: 'Doing',
|
||||
will: 'Will',
|
||||
would: 'Would',
|
||||
shall: 'Shall',
|
||||
should: 'Should',
|
||||
may: 'May',
|
||||
might: 'Might',
|
||||
can: 'Can',
|
||||
could: 'Could',
|
||||
must: 'Must',
|
||||
need: 'Need',
|
||||
dare: 'Dare',
|
||||
ought: 'Ought',
|
||||
used: 'Used',
|
||||
to: 'To',
|
||||
of: 'Of',
|
||||
in: 'In',
|
||||
for: 'For',
|
||||
on: 'On',
|
||||
with: 'With',
|
||||
at: 'At',
|
||||
by: 'By',
|
||||
from: 'From',
|
||||
into: 'Into',
|
||||
during: 'During',
|
||||
before: 'Before',
|
||||
after: 'After',
|
||||
above: 'Above',
|
||||
below: 'Below',
|
||||
between: 'Between',
|
||||
under: 'Under',
|
||||
over: 'Over',
|
||||
across: 'Across',
|
||||
through: 'Through',
|
||||
into: 'Into',
|
||||
towards: 'Towards',
|
||||
upon: 'Upon',
|
||||
about: 'About',
|
||||
against: 'Against',
|
||||
among: 'Among',
|
||||
along: 'Along',
|
||||
around: 'Around',
|
||||
beyond: 'Beyond',
|
||||
but: 'But',
|
||||
despite: 'Despite',
|
||||
except: 'Except',
|
||||
inside: 'Inside',
|
||||
outside: 'Outside',
|
||||
since: 'Since',
|
||||
until: 'Until',
|
||||
unless: 'Unless',
|
||||
whether: 'Whether',
|
||||
while: 'While',
|
||||
although: 'Although',
|
||||
because: 'Because',
|
||||
if: 'If',
|
||||
once: 'Once',
|
||||
since: 'Since',
|
||||
so: 'So',
|
||||
that: 'That',
|
||||
though: 'Though',
|
||||
till: 'Till',
|
||||
unless: 'Unless',
|
||||
until: 'Until',
|
||||
when: 'When',
|
||||
whenever: 'Whenever',
|
||||
where: 'Where',
|
||||
wherever: 'Wherever',
|
||||
whereas: 'Whereas',
|
||||
wherever: 'Wherever',
|
||||
while: 'While',
|
||||
why: 'Why',
|
||||
},
|
||||
};
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* MetonaToast Plugins - 插件系统
|
||||
* @module plugins
|
||||
* @version 2.0.0
|
||||
* @description 插件管理器 + 3 款预设插件 (keyboard / persistence / accessibility)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 插件管理器
|
||||
*/
|
||||
class PluginManager {
|
||||
constructor() {
|
||||
this.plugins = new Map();
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
register(name, plugin) {
|
||||
if (this.plugins.has(name)) {
|
||||
console.warn(`Plugin "${name}" is already registered`);
|
||||
return this;
|
||||
}
|
||||
if (!plugin || typeof plugin !== 'object' || (!plugin.name && !plugin.version)) {
|
||||
console.error(`Invalid plugin "${name}"`);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.plugins.set(name, { name, ...plugin, installed: false, enabled: true });
|
||||
|
||||
if (plugin.install) {
|
||||
try { plugin.install(this); this.plugins.get(name).installed = true; }
|
||||
catch (e) { console.error(`Failed to install plugin "${name}":`, e); }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
unregister(name) {
|
||||
const plugin = this.plugins.get(name);
|
||||
if (!plugin) return this;
|
||||
if (plugin.uninstall) {
|
||||
try { plugin.uninstall(this); } catch (e) { console.error(`Failed to uninstall plugin "${name}":`, e); }
|
||||
}
|
||||
this.plugins.delete(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
get(name) { return this.plugins.get(name) || null; }
|
||||
has(name) { return this.plugins.has(name); }
|
||||
getAll() { return Array.from(this.plugins.values()); }
|
||||
getNames() { return Array.from(this.plugins.keys()); }
|
||||
|
||||
enable(name) {
|
||||
const p = this.plugins.get(name);
|
||||
if (p) p.enabled = true;
|
||||
return this;
|
||||
}
|
||||
disable(name) {
|
||||
const p = this.plugins.get(name);
|
||||
if (p) p.enabled = false;
|
||||
return this;
|
||||
}
|
||||
isEnabled(name) {
|
||||
const p = this.plugins.get(name);
|
||||
return p ? p.enabled : false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.plugins.forEach((p, name) => {
|
||||
if (p.destroy) { try { p.destroy(this); } catch (e) {} }
|
||||
});
|
||||
this.plugins.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预设插件
|
||||
*/
|
||||
const presetPlugins = {
|
||||
|
||||
/**
|
||||
* 键盘快捷键插件 — ESC 关闭所有 Toast
|
||||
*/
|
||||
keyboard: {
|
||||
name: 'keyboard',
|
||||
version: '1.0.0',
|
||||
description: 'ESC 关闭所有 Toast',
|
||||
|
||||
_handler: null,
|
||||
|
||||
install() {
|
||||
if (typeof document === 'undefined') return;
|
||||
this._handler = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
// 点击所有关闭按钮
|
||||
document.querySelectorAll('.met-toast .met-close').forEach(btn => {
|
||||
try { btn.click(); } catch (_) {}
|
||||
});
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', this._handler);
|
||||
},
|
||||
|
||||
uninstall() {
|
||||
if (this._handler && typeof document !== 'undefined') {
|
||||
document.removeEventListener('keydown', this._handler);
|
||||
this._handler = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 持久化插件 — 自动保存/加载配置到 localStorage
|
||||
*/
|
||||
persistence: {
|
||||
name: 'persistence',
|
||||
version: '1.0.0',
|
||||
description: '自动持久化配置到 localStorage',
|
||||
storageKey: 'metona-toast-config',
|
||||
|
||||
install() {
|
||||
// 加载已保存的配置
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
const saved = localStorage.getItem(this.storageKey);
|
||||
return saved ? JSON.parse(saved) : null;
|
||||
} catch (_) { return null; }
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
save(config) {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try { localStorage.setItem(this.storageKey, JSON.stringify(config)); } catch (_) {}
|
||||
}
|
||||
},
|
||||
|
||||
uninstall() {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try { localStorage.removeItem(this.storageKey); } catch (_) {}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 无障碍插件 — 屏幕阅读器公告
|
||||
*/
|
||||
accessibility: {
|
||||
name: 'accessibility',
|
||||
version: '1.0.0',
|
||||
description: '通过屏幕阅读器朗读 Toast 内容',
|
||||
|
||||
announce(toast) {
|
||||
if (typeof document === 'undefined') return;
|
||||
const el = document.createElement('div');
|
||||
el.setAttribute('aria-live', 'assertive');
|
||||
el.setAttribute('aria-atomic', 'true');
|
||||
el.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';
|
||||
const typeNames = { success:'成功', error:'错误', warning:'警告', info:'信息', loading:'加载中', default:'通知' };
|
||||
el.textContent = `${typeNames[toast.type] || '通知'}: ${toast.title || ''} ${toast.message || ''}`;
|
||||
document.body.appendChild(el);
|
||||
setTimeout(() => { if (el.parentNode) el.parentNode.removeChild(el); }, 3000);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 插件工具
|
||||
*/
|
||||
const pluginUtils = {
|
||||
createManager() { return new PluginManager(); },
|
||||
register(name, plugin) { return defaultPluginManager.register(name, plugin); },
|
||||
unregister(name) { return defaultPluginManager.unregister(name); },
|
||||
get(name) { return defaultPluginManager.get(name); },
|
||||
has(name) { return defaultPluginManager.has(name); },
|
||||
getAll() { return defaultPluginManager.getAll(); },
|
||||
getNames() { return defaultPluginManager.getNames(); },
|
||||
enable(name) { return defaultPluginManager.enable(name); },
|
||||
disable(name) { return defaultPluginManager.disable(name); },
|
||||
isEnabled(name) { return defaultPluginManager.isEnabled(name); },
|
||||
getPreset(name) { return presetPlugins[name] || null; },
|
||||
getAllPresets() { return { ...presetPlugins }; },
|
||||
|
||||
createPlugin(config) {
|
||||
return {
|
||||
name: config.name || 'custom',
|
||||
version: config.version || '1.0.0',
|
||||
description: config.description || '',
|
||||
hooks: config.hooks || {},
|
||||
install: config.install || null,
|
||||
uninstall: config.uninstall || null,
|
||||
...config,
|
||||
};
|
||||
},
|
||||
|
||||
validatePlugin(plugin) {
|
||||
const errors = [];
|
||||
if (!plugin || typeof plugin !== 'object') errors.push('Plugin must be an object');
|
||||
if (!plugin.name && !plugin.version) errors.push('Plugin must have a name or version');
|
||||
return { valid: errors.length === 0, errors };
|
||||
},
|
||||
};
|
||||
|
||||
const defaultPluginManager = new PluginManager();
|
||||
|
||||
export { presetPlugins, pluginUtils, defaultPluginManager, PluginManager };
|
||||
+1848
File diff suppressed because it is too large
Load Diff
+722
@@ -0,0 +1,722 @@
|
||||
/**
|
||||
* MetonaToast Themes - 主题管理
|
||||
* @module themes
|
||||
* @version 2.0.0
|
||||
* @description 主题系统、自定义主题和主题切换
|
||||
*/
|
||||
|
||||
import { THEMES } from './constants.js';
|
||||
import { prefersDark } from './utils.js';
|
||||
|
||||
// 当前主题状态
|
||||
let currentTheme = 'auto';
|
||||
let themeListeners = new Set();
|
||||
let systemThemeMediaQuery = null;
|
||||
let systemThemeListener = null;
|
||||
|
||||
/**
|
||||
* 获取系统主题
|
||||
* @returns {string} 系统主题 ('light' 或 'dark')
|
||||
*/
|
||||
export const getSystemTheme = () => {
|
||||
if (typeof window === 'undefined') return 'light';
|
||||
return prefersDark() ? 'dark' : 'light';
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题(别名)
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
export const getTheme = (theme) => {
|
||||
return resolveTheme(theme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析主题
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
export const resolveTheme = (theme) => {
|
||||
if (theme === 'auto') {
|
||||
// 优先使用用户设置的全局主题,否则使用系统主题
|
||||
if (currentTheme && currentTheme !== 'auto') {
|
||||
return currentTheme;
|
||||
}
|
||||
return getSystemTheme();
|
||||
}
|
||||
return theme;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题配置
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题配置
|
||||
*/
|
||||
export const getThemeConfig = (theme) => {
|
||||
const resolved = resolveTheme(theme);
|
||||
return THEMES[resolved] || THEMES.light;
|
||||
};
|
||||
|
||||
/**
|
||||
* 应用主题
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
export const applyTheme = (theme) => {
|
||||
currentTheme = theme;
|
||||
const resolved = resolveTheme(theme);
|
||||
|
||||
// 应用主题到文档
|
||||
if (typeof document !== 'undefined') {
|
||||
document.documentElement.setAttribute('data-met-theme', resolved);
|
||||
document.documentElement.classList.remove('met-theme-light', 'met-theme-dark', 'met-theme-auto');
|
||||
document.documentElement.classList.add(`met-theme-${resolved}`);
|
||||
|
||||
// 设置CSS变量
|
||||
const config = getThemeConfig(theme);
|
||||
setThemeVariables(config);
|
||||
}
|
||||
|
||||
// 通知监听器
|
||||
notifyThemeListeners(theme, resolved);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置主题CSS变量
|
||||
* @param {Object} config - 主题配置
|
||||
*/
|
||||
export const setThemeVariables = (config) => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--met-bg', config.bg);
|
||||
root.style.setProperty('--met-text', config.text);
|
||||
root.style.setProperty('--met-border', config.border);
|
||||
root.style.setProperty('--met-shadow', config.shadow);
|
||||
root.style.setProperty('--met-hover-shadow', config.hoverShadow);
|
||||
root.style.setProperty('--met-progress-bg', config.progressBg);
|
||||
root.style.setProperty('--met-close-hover-bg', config.closeHoverBg);
|
||||
|
||||
// 设置颜色变量
|
||||
root.style.setProperty('--met-success', '#10b981');
|
||||
root.style.setProperty('--met-error', '#ef4444');
|
||||
root.style.setProperty('--met-warning', '#f59e0b');
|
||||
root.style.setProperty('--met-info', '#3b82f6');
|
||||
root.style.setProperty('--met-loading', '#6366f1');
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除主题CSS变量
|
||||
*/
|
||||
export const clearThemeVariables = () => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const root = document.documentElement;
|
||||
const variables = [
|
||||
'--met-bg',
|
||||
'--met-text',
|
||||
'--met-border',
|
||||
'--met-shadow',
|
||||
'--met-hover-shadow',
|
||||
'--met-progress-bg',
|
||||
'--met-close-hover-bg',
|
||||
'--met-success',
|
||||
'--met-error',
|
||||
'--met-warning',
|
||||
'--met-info',
|
||||
'--met-loading',
|
||||
];
|
||||
|
||||
variables.forEach((variable) => {
|
||||
root.style.removeProperty(variable);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前主题
|
||||
* @returns {string} 当前主题
|
||||
*/
|
||||
export const getCurrentTheme = () => {
|
||||
return currentTheme;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取解析后的主题
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
export const getResolvedTheme = () => {
|
||||
return resolveTheme(currentTheme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @param {string} theme - 新主题
|
||||
*/
|
||||
export const switchTheme = (theme) => {
|
||||
applyTheme(theme);
|
||||
saveTheme(theme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 切换亮色/暗色主题
|
||||
*/
|
||||
export const toggleTheme = () => {
|
||||
const resolved = getResolvedTheme();
|
||||
const newTheme = resolved === 'dark' ? 'light' : 'dark';
|
||||
switchTheme(newTheme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置为自动主题
|
||||
*/
|
||||
export const resetToAuto = () => {
|
||||
switchTheme('auto');
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存主题到本地存储
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
export const saveTheme = (theme) => {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
localStorage.setItem('metona-toast-theme', theme);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save theme:', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 从本地存储加载主题
|
||||
* @returns {string} 主题名称
|
||||
*/
|
||||
export const loadTheme = () => {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
return localStorage.getItem('metona-toast-theme') || 'auto';
|
||||
} catch (e) {
|
||||
console.warn('Failed to load theme:', e);
|
||||
}
|
||||
}
|
||||
return 'auto';
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化主题系统
|
||||
*/
|
||||
export const initTheme = () => {
|
||||
// 加载保存的主题
|
||||
const savedTheme = loadTheme();
|
||||
applyTheme(savedTheme);
|
||||
|
||||
// 监听系统主题变化
|
||||
watchSystemTheme();
|
||||
};
|
||||
|
||||
/**
|
||||
* 监听系统主题变化
|
||||
*/
|
||||
export const watchSystemTheme = () => {
|
||||
if (typeof window === 'undefined' || !window.matchMedia) return;
|
||||
|
||||
// 移除旧的监听器
|
||||
if (systemThemeMediaQuery && systemThemeListener) {
|
||||
systemThemeMediaQuery.removeEventListener('change', systemThemeListener);
|
||||
}
|
||||
|
||||
// 创建新的监听器
|
||||
systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
systemThemeListener = (e) => {
|
||||
if (currentTheme === 'auto') {
|
||||
applyTheme('auto');
|
||||
}
|
||||
};
|
||||
|
||||
systemThemeMediaQuery.addEventListener('change', systemThemeListener);
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止监听系统主题变化
|
||||
*/
|
||||
export const unwatchSystemTheme = () => {
|
||||
if (systemThemeMediaQuery && systemThemeListener) {
|
||||
systemThemeMediaQuery.removeEventListener('change', systemThemeListener);
|
||||
systemThemeMediaQuery = null;
|
||||
systemThemeListener = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
* @returns {Function} 移除监听器函数
|
||||
*/
|
||||
export const addThemeListener = (listener) => {
|
||||
themeListeners.add(listener);
|
||||
|
||||
return () => {
|
||||
themeListeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
*/
|
||||
export const removeThemeListener = (listener) => {
|
||||
themeListeners.delete(listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* 通知主题监听器
|
||||
* @param {string} theme - 主题名称
|
||||
* @param {string} resolved - 解析后的主题
|
||||
*/
|
||||
const notifyThemeListeners = (theme, resolved) => {
|
||||
themeListeners.forEach((listener) => {
|
||||
try {
|
||||
listener(theme, resolved);
|
||||
} catch (e) {
|
||||
console.error('Theme listener error:', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除所有主题监听器
|
||||
*/
|
||||
export const clearThemeListeners = () => {
|
||||
themeListeners.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
* @param {Object} config - 主题配置
|
||||
*/
|
||||
export const registerTheme = (name, config) => {
|
||||
THEMES[name] = {
|
||||
bg: config.bg || THEMES.light.bg,
|
||||
text: config.text || THEMES.light.text,
|
||||
border: config.border || THEMES.light.border,
|
||||
shadow: config.shadow || THEMES.light.shadow,
|
||||
hoverShadow: config.hoverShadow || THEMES.light.hoverShadow,
|
||||
progressBg: config.progressBg || THEMES.light.progressBg,
|
||||
closeHoverBg: config.closeHoverBg || THEMES.light.closeHoverBg,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 注销自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
*/
|
||||
export const unregisterTheme = (name) => {
|
||||
if (name === 'light' || name === 'dark' || name === 'auto') {
|
||||
console.warn('Cannot unregister built-in theme:', name);
|
||||
return;
|
||||
}
|
||||
|
||||
delete THEMES[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有主题
|
||||
* @returns {Object} 主题映射
|
||||
*/
|
||||
export const getAllThemes = () => {
|
||||
return { ...THEMES };
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题名称列表
|
||||
* @returns {Array} 主题名称数组
|
||||
*/
|
||||
export const getThemeNames = () => {
|
||||
return Object.keys(THEMES);
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查主题是否存在
|
||||
* @param {string} name - 主题名称
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
export const hasTheme = (name) => {
|
||||
return name in THEMES;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取主题预览
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题预览
|
||||
*/
|
||||
export const getThemePreview = (theme) => {
|
||||
const config = getThemeConfig(theme);
|
||||
const resolved = resolveTheme(theme);
|
||||
|
||||
return {
|
||||
name: theme,
|
||||
resolved,
|
||||
colors: {
|
||||
background: config.bg,
|
||||
text: config.text,
|
||||
border: config.border,
|
||||
},
|
||||
isDark: resolved === 'dark',
|
||||
isLight: resolved === 'light',
|
||||
isAuto: theme === 'auto',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} CSS字符串
|
||||
*/
|
||||
export const generateThemeCSS = (theme) => {
|
||||
const config = getThemeConfig(theme);
|
||||
const resolved = resolveTheme(theme);
|
||||
|
||||
return `
|
||||
.met-theme-${resolved} {
|
||||
--met-bg: ${config.bg};
|
||||
--met-text: ${config.text};
|
||||
--met-border: ${config.border};
|
||||
--met-shadow: ${config.shadow};
|
||||
--met-hover-shadow: ${config.hoverShadow};
|
||||
--met-progress-bg: ${config.progressBg};
|
||||
--met-close-hover-bg: ${config.closeHoverBg};
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 应用主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
export const applyThemeCSS = (theme) => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const css = generateThemeCSS(theme);
|
||||
const styleId = 'metona-toast-theme-css';
|
||||
|
||||
// 移除旧样式
|
||||
const oldStyle = document.getElementById(styleId);
|
||||
if (oldStyle) {
|
||||
oldStyle.remove();
|
||||
}
|
||||
|
||||
// 添加新样式
|
||||
const style = document.createElement('style');
|
||||
style.id = styleId;
|
||||
style.textContent = css;
|
||||
document.head.appendChild(style);
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除主题CSS
|
||||
*/
|
||||
export const removeThemeCSS = () => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
const styleId = 'metona-toast-theme-css';
|
||||
const style = document.getElementById(styleId);
|
||||
if (style) {
|
||||
style.remove();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 主题工具
|
||||
*/
|
||||
export const themeUtils = {
|
||||
/**
|
||||
* 获取系统主题
|
||||
* @returns {string} 系统主题
|
||||
*/
|
||||
getSystemTheme() {
|
||||
return getSystemTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析主题
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
resolveTheme(theme) {
|
||||
return resolveTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题配置
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题配置
|
||||
*/
|
||||
getThemeConfig(theme) {
|
||||
return getThemeConfig(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用主题
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
applyTheme(theme) {
|
||||
applyTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前主题
|
||||
* @returns {string} 当前主题
|
||||
*/
|
||||
getCurrentTheme() {
|
||||
return getCurrentTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取解析后的主题
|
||||
* @returns {string} 解析后的主题
|
||||
*/
|
||||
getResolvedTheme() {
|
||||
return getResolvedTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @param {string} theme - 新主题
|
||||
*/
|
||||
switchTheme(theme) {
|
||||
switchTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换亮色/暗色主题
|
||||
*/
|
||||
toggleTheme() {
|
||||
toggleTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置为自动主题
|
||||
*/
|
||||
resetToAuto() {
|
||||
resetToAuto();
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化主题系统
|
||||
*/
|
||||
initTheme() {
|
||||
initTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听系统主题变化
|
||||
*/
|
||||
watchSystemTheme() {
|
||||
watchSystemTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止监听系统主题变化
|
||||
*/
|
||||
unwatchSystemTheme() {
|
||||
unwatchSystemTheme();
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
* @returns {Function} 移除监听器函数
|
||||
*/
|
||||
addThemeListener(listener) {
|
||||
return addThemeListener(listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除主题监听器
|
||||
* @param {Function} listener - 监听器函数
|
||||
*/
|
||||
removeThemeListener(listener) {
|
||||
removeThemeListener(listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除所有主题监听器
|
||||
*/
|
||||
clearThemeListeners() {
|
||||
clearThemeListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* 注册自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
* @param {Object} config - 主题配置
|
||||
*/
|
||||
registerTheme(name, config) {
|
||||
registerTheme(name, config);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注销自定义主题
|
||||
* @param {string} name - 主题名称
|
||||
*/
|
||||
unregisterTheme(name) {
|
||||
unregisterTheme(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有主题
|
||||
* @returns {Object} 主题映射
|
||||
*/
|
||||
getAllThemes() {
|
||||
return getAllThemes();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题名称列表
|
||||
* @returns {Array} 主题名称数组
|
||||
*/
|
||||
getThemeNames() {
|
||||
return getThemeNames();
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查主题是否存在
|
||||
* @param {string} name - 主题名称
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
hasTheme(name) {
|
||||
return hasTheme(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题预览
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {Object} 主题预览
|
||||
*/
|
||||
getThemePreview(theme) {
|
||||
return getThemePreview(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
* @returns {string} CSS字符串
|
||||
*/
|
||||
generateThemeCSS(theme) {
|
||||
return generateThemeCSS(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 应用主题CSS
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
applyThemeCSS(theme) {
|
||||
applyThemeCSS(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除主题CSS
|
||||
*/
|
||||
removeThemeCSS() {
|
||||
removeThemeCSS();
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存主题
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
saveTheme(theme) {
|
||||
saveTheme(theme);
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载主题
|
||||
* @returns {string} 主题名称
|
||||
*/
|
||||
loadTheme() {
|
||||
return loadTheme();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 预设主题
|
||||
*/
|
||||
export const presetThemes = {
|
||||
/**
|
||||
* 浅色主题
|
||||
*/
|
||||
light: {
|
||||
name: '浅色',
|
||||
description: '明亮清晰的主题',
|
||||
config: THEMES.light,
|
||||
},
|
||||
|
||||
/**
|
||||
* 深色主题
|
||||
*/
|
||||
dark: {
|
||||
name: '深色',
|
||||
description: '护眼舒适的暗色主题',
|
||||
config: THEMES.dark,
|
||||
},
|
||||
|
||||
/**
|
||||
* 自动主题
|
||||
*/
|
||||
auto: {
|
||||
name: '自动',
|
||||
description: '跟随系统主题设置',
|
||||
config: 'auto',
|
||||
},
|
||||
|
||||
warm: {
|
||||
name: '暖色',
|
||||
description: '温馨舒适的暖色主题',
|
||||
config: THEMES.warm,
|
||||
},
|
||||
};
|
||||
|
||||
// 注册预设主题
|
||||
Object.entries(presetThemes).forEach(([name, theme]) => {
|
||||
if (name !== 'auto' && theme.config !== 'auto') {
|
||||
THEMES[name] = theme.config;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建主题管理器
|
||||
* @returns {Object} 主题管理器
|
||||
*/
|
||||
export const createThemeManager = () => {
|
||||
return {
|
||||
getSystemTheme,
|
||||
resolveTheme,
|
||||
getThemeConfig,
|
||||
applyTheme,
|
||||
getCurrentTheme,
|
||||
getResolvedTheme,
|
||||
switchTheme,
|
||||
toggleTheme,
|
||||
resetToAuto,
|
||||
initTheme,
|
||||
watchSystemTheme,
|
||||
unwatchSystemTheme,
|
||||
addThemeListener,
|
||||
removeThemeListener,
|
||||
clearThemeListeners,
|
||||
registerTheme,
|
||||
unregisterTheme,
|
||||
getAllThemes,
|
||||
getThemeNames,
|
||||
hasTheme,
|
||||
getThemePreview,
|
||||
generateThemeCSS,
|
||||
applyThemeCSS,
|
||||
removeThemeCSS,
|
||||
saveTheme,
|
||||
loadTheme,
|
||||
};
|
||||
};
|
||||
|
||||
export { themeUtils as default };
|
||||
+1062
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user