vite-plugin-trae-solo-badge
Version:
TRAE Badge component - A beautiful 'Powered by TRAE' badge for web applications
192 lines (159 loc) • 5.38 kB
text/typescript
/**
* TRAE Badge 主类
*/
import type { TraeBadgeOptions, TraeBadgeInstance, LogoVariant } from './types';
import { BADGE_CSS, TRAE_LOGOS, CANCEL_SVG_DARK, CANCEL_SVG_LIGHT, DEFAULT_OPTIONS, SHARE_SVG } from './constants';
import { autoSelectTheme } from './theme';
export class TraeBadge implements TraeBadgeInstance {
private options: TraeBadgeOptions;
private element: HTMLElement | null = null;
private styleElement: HTMLStyleElement | null = null;
private badgeId: string;
private originalVariant?: TraeBadgeOptions['variant']; // 保存原始主题设置
constructor(options: TraeBadgeOptions = {}) {
this.options = { ...DEFAULT_OPTIONS, ...options };
this.originalVariant = options.variant; // 保存用户设置的原始主题
this.badgeId = `trae-badge-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
this.init();
}
private init(): void {
this.injectStyles();
this.createBadge();
}
private injectStyles(): void {
// 避免重复注入样式
if (document.getElementById('trae-badge-styles')) {
return;
}
this.styleElement = document.createElement('style');
this.styleElement.id = 'trae-badge-styles';
this.styleElement.textContent = BADGE_CSS;
document.head.appendChild(this.styleElement);
}
private createBadge(): void {
// 移除已存在的 Badge
this.destroy();
if (this.options.disabled) {
return;
}
// 如果启用了自动主题选择,则自动选择主题
if (this.options.autoTheme) {
const autoThemeTarget = this.options.autoThemeTarget || this.options.container;
this.options.variant = autoSelectTheme(autoThemeTarget);
}
this.element = document.createElement('div');
this.element.id = this.badgeId;
this.element.className = this.getBadgeClassName();
const isDarkBackground = this.options.variant === 'dark';
const logoType: LogoVariant = isDarkBackground ? 'light' : 'dark';
this.element.innerHTML = `
<div class="trae-badge-content">
<div class="trae-badge-logo">${TRAE_LOGOS[logoType]}</div>
<div class="trae-badge-share">${SHARE_SVG}</div>
</div>
<div class="trae-badge-cancel">
${isDarkBackground ? CANCEL_SVG_LIGHT : CANCEL_SVG_DARK}
</div>
`;
// 应用自定义样式
if (this.options.style) {
Object.assign(this.element.style, this.options.style);
}
// 添加取消按钮点击事件
this.addCancelEvent();
// 添加点击事件(点击badge本身)
if (this.options.clickable) {
this.element.style.cursor = 'pointer';
this.element.addEventListener('click', this.handleClick.bind(this));
}
// 确定容器
let container: HTMLElement = document.body;
if (this.options.container) {
if (typeof this.options.container === 'string') {
const found = document.querySelector(this.options.container);
if (found) {
container = found as HTMLElement;
}
} else {
container = this.options.container;
}
}
container.appendChild(this.element);
}
private addCancelEvent(): void {
if (!this.element) return;
const cancelButton = this.element.querySelector('.trae-badge-cancel');
if (cancelButton) {
cancelButton.addEventListener('click', (e) => {
e.stopPropagation(); // 防止触发 badge 本身的点击事件
this.handleCancel();
});
}
}
private handleCancel(): void {
// 隐藏/销毁 badge
this.destroy();
}
private getBadgeClassName(): string {
const classes = [
'trae-badge',
`trae-badge--${this.options.variant}`,
`trae-badge--${this.options.position}`
];
if (this.options.className) {
classes.push(this.options.className);
}
return classes.join(' ');
}
private handleClick(): void {
if (this.options.clickUrl) {
window.open(this.options.clickUrl, '_blank');
} else {
console.log('TRAE Badge clicked');
}
}
// 公共方法
updateOptions(newOptions: Partial<TraeBadgeOptions>): void {
// 更新原始主题设置
if (newOptions.variant !== undefined) {
this.originalVariant = newOptions.variant;
}
this.options = { ...this.options, ...newOptions };
this.createBadge();
}
destroy(): void {
if (this.element) {
this.element.remove();
this.element = null;
}
}
getOptions(): TraeBadgeOptions {
return { ...this.options };
}
getElement(): HTMLElement | null {
return this.element;
}
/**
* 刷新自动主题(如果启用)
* 重新检测背景色并更新主题
*/
refreshAutoTheme(): void {
if (this.options.autoTheme) {
const autoThemeTarget = this.options.autoThemeTarget || this.options.container;
const newVariant = autoSelectTheme(autoThemeTarget);
// 只有当主题发生变化时才重新创建badge
if (newVariant !== this.options.variant) {
this.options.variant = newVariant;
this.createBadge();
}
}
}
// 静态方法
static destroyAll(): void {
const badges = document.querySelectorAll('[id^="trae-badge-"]');
badges.forEach(badge => badge.remove());
}
static isStylesInjected(): boolean {
return !!document.getElementById('trae-badge-styles');
}
}