@windrun-huaiin/third-ui 7.3.2 → 7.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/main/faq.js +12 -0
  2. package/dist/main/faq.mjs +12 -0
  3. package/dist/main/gallery.js +12 -0
  4. package/dist/main/gallery.mjs +12 -0
  5. package/dist/main/index.d.ts +2 -0
  6. package/dist/main/index.js +4 -0
  7. package/dist/main/index.mjs +2 -0
  8. package/dist/main/money-price/money-price-button.d.ts +2 -0
  9. package/dist/main/money-price/money-price-button.js +96 -0
  10. package/dist/main/money-price/money-price-button.mjs +94 -0
  11. package/dist/main/money-price/money-price-config.d.ts +8 -0
  12. package/dist/main/money-price/money-price-config.js +223 -0
  13. package/dist/main/money-price/money-price-config.mjs +219 -0
  14. package/dist/main/money-price/money-price-interactive.d.ts +2 -0
  15. package/dist/main/money-price/money-price-interactive.js +293 -0
  16. package/dist/main/money-price/money-price-interactive.mjs +291 -0
  17. package/dist/main/money-price/money-price-types.d.ts +116 -0
  18. package/dist/main/money-price/money-price-types.js +14 -0
  19. package/dist/main/money-price/money-price-types.mjs +14 -0
  20. package/dist/main/money-price/money-price.d.ts +2 -0
  21. package/dist/main/money-price/money-price.js +93 -0
  22. package/dist/main/money-price/money-price.mjs +91 -0
  23. package/dist/main/price-plan.js +13 -0
  24. package/dist/main/price-plan.mjs +13 -0
  25. package/dist/main/server.d.ts +4 -0
  26. package/dist/main/server.js +11 -0
  27. package/dist/main/server.mjs +3 -0
  28. package/dist/node_modules/.pnpm/cose-base@1.0.3/node_modules/cose-base/cose-base.js +1 -1
  29. package/dist/node_modules/.pnpm/cose-base@2.2.0/node_modules/cose-base/cose-base.js +1 -1
  30. package/dist/node_modules/.pnpm/layout-base@1.0.2/node_modules/layout-base/layout-base.js +1 -1
  31. package/dist/node_modules/.pnpm/layout-base@2.0.1/node_modules/layout-base/layout-base.js +1 -1
  32. package/package.json +1 -1
  33. package/src/main/index.ts +5 -1
  34. package/src/main/money-price/money-price-button.tsx +105 -0
  35. package/src/main/money-price/money-price-config.ts +229 -0
  36. package/src/main/money-price/money-price-interactive.tsx +356 -0
  37. package/src/main/money-price/money-price-types.ts +138 -0
  38. package/src/main/money-price/money-price.tsx +307 -0
  39. package/src/main/server.ts +25 -1
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Money Price Configuration
3
+ * 价格组件配置文件
4
+ */
5
+ // 示例配置
6
+ const moneyPriceConfig = {
7
+ paymentProviders: {
8
+ stripe: {
9
+ provider: 'stripe',
10
+ enabled: true,
11
+ products: {
12
+ free: {
13
+ key: 'free',
14
+ name: 'free', // Just a key, actual display name comes from translation
15
+ plans: {
16
+ monthly: {
17
+ priceId: 'free',
18
+ amount: 0,
19
+ currency: 'usd',
20
+ credits: 0
21
+ },
22
+ yearly: {
23
+ priceId: 'free',
24
+ amount: 0,
25
+ currency: 'usd',
26
+ credits: 0
27
+ }
28
+ }
29
+ },
30
+ pro: {
31
+ key: 'pro',
32
+ name: 'pro', // Just a key, actual display name comes from translation
33
+ plans: {
34
+ monthly: {
35
+ priceId: process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID || 'price_pro_monthly',
36
+ amount: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_AMOUNT) || 10,
37
+ currency: process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_CURRENCY || 'usd',
38
+ credits: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_CREDITS) || 100
39
+ },
40
+ yearly: {
41
+ priceId: process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_PRICE_ID || 'price_pro_yearly',
42
+ amount: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_AMOUNT) || 96,
43
+ originalAmount: 120,
44
+ discountPercent: 20,
45
+ currency: process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_CURRENCY || 'usd',
46
+ credits: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_CREDITS) || 1200
47
+ }
48
+ }
49
+ },
50
+ ultra: {
51
+ key: 'ultra',
52
+ name: 'ultra', // Just a key, actual display name comes from translation
53
+ plans: {
54
+ monthly: {
55
+ priceId: process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_PRICE_ID || 'price_ultra_monthly',
56
+ amount: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_AMOUNT) || 20,
57
+ currency: process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_CURRENCY || 'usd',
58
+ credits: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_CREDITS) || 250
59
+ },
60
+ yearly: {
61
+ priceId: process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_PRICE_ID || 'price_ultra_yearly',
62
+ amount: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_AMOUNT) || 192,
63
+ originalAmount: 240,
64
+ discountPercent: 20,
65
+ currency: process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_CURRENCY || 'usd',
66
+ credits: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_CREDITS) || 3000
67
+ }
68
+ }
69
+ }
70
+ }
71
+ },
72
+ alipay: {
73
+ provider: 'alipay',
74
+ enabled: false,
75
+ products: {
76
+ free: {
77
+ key: 'free',
78
+ name: 'free',
79
+ plans: {
80
+ monthly: {
81
+ priceId: 'free',
82
+ amount: 0,
83
+ currency: 'cny',
84
+ credits: 0
85
+ },
86
+ yearly: {
87
+ priceId: 'free',
88
+ amount: 0,
89
+ currency: 'cny',
90
+ credits: 0
91
+ }
92
+ }
93
+ },
94
+ pro: {
95
+ key: 'pro',
96
+ name: 'pro',
97
+ plans: {
98
+ monthly: {
99
+ priceId: 'alipay_pro_monthly',
100
+ amount: 70,
101
+ currency: 'cny',
102
+ credits: 100
103
+ },
104
+ yearly: {
105
+ priceId: 'alipay_pro_yearly',
106
+ amount: 672,
107
+ originalAmount: 840,
108
+ discountPercent: 20,
109
+ currency: 'cny',
110
+ credits: 1200
111
+ }
112
+ }
113
+ },
114
+ ultra: {
115
+ key: 'ultra',
116
+ name: 'ultra',
117
+ plans: {
118
+ monthly: {
119
+ priceId: 'alipay_ultra_monthly',
120
+ amount: 140,
121
+ currency: 'cny',
122
+ credits: 250
123
+ },
124
+ yearly: {
125
+ priceId: 'alipay_ultra_yearly',
126
+ amount: 1344,
127
+ originalAmount: 1680,
128
+ discountPercent: 20,
129
+ currency: 'cny',
130
+ credits: 3000
131
+ }
132
+ }
133
+ }
134
+ }
135
+ },
136
+ wechat: {
137
+ provider: 'wechat',
138
+ enabled: false,
139
+ products: {
140
+ free: {
141
+ key: 'free',
142
+ name: 'free',
143
+ plans: {
144
+ monthly: {
145
+ priceId: 'free',
146
+ amount: 0,
147
+ currency: 'cny',
148
+ credits: 0
149
+ },
150
+ yearly: {
151
+ priceId: 'free',
152
+ amount: 0,
153
+ currency: 'cny',
154
+ credits: 0
155
+ }
156
+ }
157
+ },
158
+ pro: {
159
+ key: 'pro',
160
+ name: 'pro',
161
+ plans: {
162
+ monthly: {
163
+ priceId: 'wechat_pro_monthly',
164
+ amount: 70,
165
+ currency: 'cny',
166
+ credits: 100
167
+ },
168
+ yearly: {
169
+ priceId: 'wechat_pro_yearly',
170
+ amount: 672,
171
+ originalAmount: 840,
172
+ discountPercent: 20,
173
+ currency: 'cny',
174
+ credits: 1200
175
+ }
176
+ }
177
+ },
178
+ ultra: {
179
+ key: 'ultra',
180
+ name: 'ultra',
181
+ plans: {
182
+ monthly: {
183
+ priceId: 'wechat_ultra_monthly',
184
+ amount: 140,
185
+ currency: 'cny',
186
+ credits: 250
187
+ },
188
+ yearly: {
189
+ priceId: 'wechat_ultra_yearly',
190
+ amount: 1344,
191
+ originalAmount: 1680,
192
+ discountPercent: 20,
193
+ currency: 'cny',
194
+ credits: 3000
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
200
+ },
201
+ activeProvider: process.env.NEXT_PUBLIC_ACTIVE_PAYMENT_PROVIDER || 'stripe',
202
+ display: {
203
+ currency: '$',
204
+ locale: 'en',
205
+ minFeaturesCount: 4
206
+ }
207
+ };
208
+ // 辅助函数:获取当前激活的支付供应商配置
209
+ function getActiveProviderConfig(config) {
210
+ const provider = config.activeProvider;
211
+ return config.paymentProviders[provider];
212
+ }
213
+ // 辅助函数:获取特定产品的价格信息
214
+ function getProductPricing(productKey, billingType, provider, config) {
215
+ const providerConfig = config.paymentProviders[provider];
216
+ return providerConfig.products[productKey].plans[billingType];
217
+ }
218
+
219
+ export { getActiveProviderConfig, getProductPricing, moneyPriceConfig };
@@ -0,0 +1,2 @@
1
+ import { type MoneyPriceInteractiveProps } from './money-price-types';
2
+ export declare function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath }: MoneyPriceInteractiveProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,293 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var tslib_es6 = require('../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.js');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var nextjs = require('@clerk/nextjs');
7
+ var React = require('react');
8
+ var fingerprintProvider = require('../../clerk/fingerprint/fingerprint-provider.js');
9
+ var utils = require('@windrun-huaiin/lib/utils');
10
+ var navigation = require('next/navigation');
11
+ var client = require('react-dom/client');
12
+ var moneyPriceButton = require('./money-price-button.js');
13
+ var moneyPriceConfig = require('./money-price-config.js');
14
+ var moneyPriceTypes = require('./money-price-types.js');
15
+
16
+ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath }) {
17
+ var _a, _b, _c, _d;
18
+ const fingerprintContext = fingerprintProvider.useFingerprintContextSafe();
19
+ const { redirectToSignIn } = nextjs.useClerk();
20
+ const router = navigation.useRouter();
21
+ const [billingType, setBillingType] = React.useState(data.billingSwitch.defaultKey);
22
+ const [isProcessing, setIsProcessing] = React.useState(false);
23
+ const [tooltip, setTooltip] = React.useState({ show: false, content: '', x: 0, y: 0 });
24
+ // 确定用户状态
25
+ const getUserState = () => {
26
+ var _a, _b;
27
+ if (!fingerprintContext)
28
+ return moneyPriceTypes.UserState.Anonymous;
29
+ const { xUser, xSubscription } = fingerprintContext;
30
+ if (!(xUser === null || xUser === void 0 ? void 0 : xUser.clerkUserId))
31
+ return moneyPriceTypes.UserState.Anonymous;
32
+ if (!(xSubscription === null || xSubscription === void 0 ? void 0 : xSubscription.status) || xSubscription.status === 'free')
33
+ return moneyPriceTypes.UserState.FreeUser;
34
+ if ((_a = xSubscription.priceName) === null || _a === void 0 ? void 0 : _a.includes('Pro'))
35
+ return moneyPriceTypes.UserState.ProUser;
36
+ if ((_b = xSubscription.priceName) === null || _b === void 0 ? void 0 : _b.includes('Ultra'))
37
+ return moneyPriceTypes.UserState.UltraUser;
38
+ return moneyPriceTypes.UserState.FreeUser;
39
+ };
40
+ const userContext = {
41
+ isAuthenticated: !!((_a = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xUser) === null || _a === void 0 ? void 0 : _a.clerkUserId),
42
+ subscriptionStatus: getUserState(),
43
+ subscriptionType: ((_c = (_b = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _b === void 0 ? void 0 : _b.priceId) === null || _c === void 0 ? void 0 : _c.includes('yearly')) ? 'yearly' : 'monthly',
44
+ subscriptionEndDate: (_d = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _d === void 0 ? void 0 : _d.subPeriodEnd
45
+ };
46
+ // 处理登录
47
+ const handleLogin = () => {
48
+ if (signInPath) {
49
+ router.push(signInPath);
50
+ }
51
+ else {
52
+ redirectToSignIn();
53
+ }
54
+ };
55
+ // 处理升级
56
+ const handleUpgrade = (plan, billingType) => tslib_es6.__awaiter(this, void 0, void 0, function* () {
57
+ // 如果没有配置 API 端点,跳转到首页
58
+ if (!upgradeApiEndpoint) {
59
+ router.push('/');
60
+ return;
61
+ }
62
+ setIsProcessing(true);
63
+ try {
64
+ // 获取价格配置
65
+ const pricing = moneyPriceConfig.getProductPricing(plan, billingType, config.activeProvider, config);
66
+ // 调用 API 创建支付会话
67
+ const response = yield fetch(upgradeApiEndpoint, {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ },
72
+ body: JSON.stringify({
73
+ priceId: pricing.priceId,
74
+ plan: plan,
75
+ billingType: billingType,
76
+ provider: config.activeProvider
77
+ })
78
+ });
79
+ const result = yield response.json();
80
+ if (result.success && result.checkoutUrl) {
81
+ // 跳转到支付页面
82
+ window.location.href = result.checkoutUrl;
83
+ }
84
+ else {
85
+ console.error('Failed to create checkout session:', result.error);
86
+ }
87
+ }
88
+ catch (error) {
89
+ console.error('Error during upgrade:', error);
90
+ }
91
+ finally {
92
+ setIsProcessing(false);
93
+ }
94
+ });
95
+ // 更新价格显示
96
+ const updatePriceDisplay = (newBillingType) => {
97
+ const providerConfig = moneyPriceConfig.getActiveProviderConfig(config);
98
+ data.plans.forEach((plan) => {
99
+ const productConfig = providerConfig.products[plan.key];
100
+ const pricing = productConfig.plans[newBillingType];
101
+ // 更新价格值
102
+ const priceValueElement = document.querySelector(`[data-price-value="${plan.key}"]`);
103
+ if (priceValueElement) {
104
+ if (pricing.amount === 0) {
105
+ priceValueElement.textContent = 'Free';
106
+ }
107
+ else {
108
+ priceValueElement.textContent = `${data.currency}${pricing.amount}`;
109
+ }
110
+ }
111
+ // 更新单位
112
+ const priceUnitElement = document.querySelector(`[data-price-unit="${plan.key}"]`);
113
+ if (priceUnitElement) {
114
+ const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
115
+ priceUnitElement.textContent = (billingOption === null || billingOption === void 0 ? void 0 : billingOption.unit) || '/month';
116
+ }
117
+ // 更新原价和折扣
118
+ const priceOriginalElement = document.querySelector(`[data-price-original="${plan.key}"]`);
119
+ const priceDiscountElement = document.querySelector(`[data-price-discount="${plan.key}"]`);
120
+ if (pricing.originalAmount && pricing.discountPercent) {
121
+ if (priceOriginalElement) {
122
+ priceOriginalElement.style.display = 'inline';
123
+ priceOriginalElement.textContent = `${data.currency}${pricing.originalAmount}`;
124
+ }
125
+ if (priceDiscountElement) {
126
+ priceDiscountElement.style.display = 'inline';
127
+ const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
128
+ if (billingOption === null || billingOption === void 0 ? void 0 : billingOption.discountText) {
129
+ priceDiscountElement.textContent = billingOption.discountText.replace('{percent}', String(pricing.discountPercent));
130
+ }
131
+ }
132
+ }
133
+ else {
134
+ if (priceOriginalElement)
135
+ priceOriginalElement.style.display = 'none';
136
+ if (priceDiscountElement)
137
+ priceDiscountElement.style.display = 'none';
138
+ }
139
+ // 更新副标题
140
+ const priceSubtitleElement = document.querySelector(`[data-price-subtitle="${plan.key}"]`);
141
+ if (priceSubtitleElement && plan.showBillingSubTitle !== false) {
142
+ const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
143
+ priceSubtitleElement.textContent = (billingOption === null || billingOption === void 0 ? void 0 : billingOption.subTitle) || '';
144
+ }
145
+ });
146
+ };
147
+ // 更新按钮样式
148
+ const updateButtonStyles = (newBillingType) => {
149
+ const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
150
+ const yearlyButton = document.querySelector('[data-billing-button="yearly"]');
151
+ const activeClasses = 'text-white bg-gradient-to-r from-purple-400 to-pink-500 hover:from-purple-500 hover:to-pink-600 dark:from-purple-500 dark:to-pink-600 dark:hover:from-purple-600 rounded-full shadow-sm';
152
+ const inactiveClasses = 'text-gray-800 dark:text-gray-200 hover:text-gray-900 dark:hover:text-gray-100 rounded-full';
153
+ if (monthlyButton && yearlyButton) {
154
+ if (newBillingType === 'monthly') {
155
+ monthlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', activeClasses);
156
+ yearlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', inactiveClasses);
157
+ }
158
+ else {
159
+ monthlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', inactiveClasses);
160
+ yearlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', activeClasses);
161
+ }
162
+ }
163
+ };
164
+ // 更新折扣信息
165
+ const updateDiscountInfo = (newBillingType) => {
166
+ const discountInfoElement = document.querySelector('[data-discount-info]');
167
+ if (!discountInfoElement)
168
+ return;
169
+ const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
170
+ const providerConfig = moneyPriceConfig.getActiveProviderConfig(config);
171
+ // 检查是否有折扣
172
+ let hasDiscount = false;
173
+ let discountPercent = 0;
174
+ ['pro', 'ultra'].forEach(planKey => {
175
+ const product = providerConfig.products[planKey];
176
+ const pricing = product.plans[newBillingType];
177
+ if (pricing.discountPercent) {
178
+ hasDiscount = true;
179
+ discountPercent = pricing.discountPercent;
180
+ }
181
+ });
182
+ // 清空内容
183
+ discountInfoElement.innerHTML = '';
184
+ if (hasDiscount && (billingOption === null || billingOption === void 0 ? void 0 : billingOption.discountText)) {
185
+ const discountBadge = document.createElement('span');
186
+ discountBadge.className = 'px-2 py-1 text-xs rounded bg-yellow-100 text-yellow-800 font-semibold align-middle text-center inline-flex items-center justify-center whitespace-nowrap';
187
+ discountBadge.textContent = billingOption.discountText.replace('{percent}', String(discountPercent));
188
+ discountInfoElement.appendChild(discountBadge);
189
+ }
190
+ };
191
+ // 动态替换按钮
192
+ React.useEffect(() => {
193
+ data.plans.forEach((plan) => {
194
+ const placeholder = document.querySelector(`[data-button-placeholder="${plan.key}"]`);
195
+ if (placeholder) {
196
+ const root = client.createRoot(placeholder);
197
+ root.render(jsxRuntime.jsx(moneyPriceButton.MoneyPriceButton, { planKey: plan.key, userContext: userContext, billingType: billingType, onLogin: handleLogin, onUpgrade: handleUpgrade, texts: data.buttonTexts, isProcessing: isProcessing }));
198
+ }
199
+ });
200
+ }, [userContext, billingType, isProcessing]);
201
+ // 处理月付/年付切换和 tooltip 功能
202
+ React.useEffect(() => {
203
+ const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
204
+ const yearlyButton = document.querySelector('[data-billing-button="yearly"]');
205
+ const handleMonthlyClick = () => {
206
+ setBillingType('monthly');
207
+ updatePriceDisplay('monthly');
208
+ updateButtonStyles('monthly');
209
+ updateDiscountInfo('monthly');
210
+ };
211
+ const handleYearlyClick = () => {
212
+ setBillingType('yearly');
213
+ updatePriceDisplay('yearly');
214
+ updateButtonStyles('yearly');
215
+ updateDiscountInfo('yearly');
216
+ };
217
+ if (monthlyButton) {
218
+ monthlyButton.addEventListener('click', handleMonthlyClick);
219
+ }
220
+ if (yearlyButton) {
221
+ yearlyButton.addEventListener('click', handleYearlyClick);
222
+ }
223
+ // 添加 tooltip 功能
224
+ const tooltipHandlers = [];
225
+ data.plans.forEach((plan) => {
226
+ var _a;
227
+ (_a = plan.features) === null || _a === void 0 ? void 0 : _a.forEach((feature, i) => {
228
+ if (feature === null || feature === void 0 ? void 0 : feature.tooltip) {
229
+ const tooltipTrigger = document.querySelector(`[data-tooltip-trigger="${plan.key}-${i}"]`);
230
+ if (tooltipTrigger) {
231
+ const handlers = {
232
+ mouseenter: (e) => {
233
+ setTooltip({
234
+ show: true,
235
+ content: feature.tooltip,
236
+ x: e.clientX,
237
+ y: e.clientY
238
+ });
239
+ },
240
+ mousemove: (e) => {
241
+ setTooltip(prev => (Object.assign(Object.assign({}, prev), { x: e.clientX, y: e.clientY })));
242
+ },
243
+ mouseleave: () => {
244
+ setTooltip(prev => (Object.assign(Object.assign({}, prev), { show: false })));
245
+ }
246
+ };
247
+ tooltipTrigger.addEventListener('mouseenter', handlers.mouseenter);
248
+ tooltipTrigger.addEventListener('mousemove', handlers.mousemove);
249
+ tooltipTrigger.addEventListener('mouseleave', handlers.mouseleave);
250
+ tooltipHandlers.push({ element: tooltipTrigger, handlers });
251
+ }
252
+ }
253
+ });
254
+ });
255
+ // 初始化价格显示
256
+ updatePriceDisplay(billingType);
257
+ updateDiscountInfo(billingType);
258
+ // 清理
259
+ return () => {
260
+ if (monthlyButton) {
261
+ monthlyButton.removeEventListener('click', handleMonthlyClick);
262
+ }
263
+ if (yearlyButton) {
264
+ yearlyButton.removeEventListener('click', handleYearlyClick);
265
+ }
266
+ // 清理 tooltip 事件监听器
267
+ tooltipHandlers.forEach(({ element, handlers }) => {
268
+ element.removeEventListener('mouseenter', handlers.mouseenter);
269
+ element.removeEventListener('mousemove', handlers.mousemove);
270
+ element.removeEventListener('mouseleave', handlers.mouseleave);
271
+ });
272
+ };
273
+ }, [data]);
274
+ // Tooltip 组件
275
+ const Tooltip = ({ show, content, x, y }) => {
276
+ if (!show)
277
+ return null;
278
+ const style = {
279
+ position: 'fixed',
280
+ left: Math.max(8, x),
281
+ top: Math.max(8, y),
282
+ zIndex: 9999,
283
+ maxWidth: 200,
284
+ transform: 'translateY(-50%)',
285
+ pointerEvents: 'none',
286
+ whiteSpace: 'pre-line',
287
+ };
288
+ return (jsxRuntime.jsx("div", { style: style, className: "bg-gray-700 dark:bg-gray-200 text-gray-100 dark:text-gray-800 text-xs leading-relaxed px-3 py-2 rounded-lg shadow-lg border border-gray-300 dark:border-gray-600 backdrop-blur-sm", children: content }));
289
+ };
290
+ return jsxRuntime.jsx(Tooltip, Object.assign({}, tooltip));
291
+ }
292
+
293
+ exports.MoneyPriceInteractive = MoneyPriceInteractive;