@windrun-huaiin/third-ui 10.1.2 → 11.0.0

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.
package/LICENSE CHANGED
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
@@ -118,7 +118,7 @@ function createFingerprintHeaders() {
118
118
  return tslib_es6.__awaiter(this, void 0, void 0, function* () {
119
119
  const fingerprintId = yield getOrGenerateFingerprintId();
120
120
  return {
121
- FINGERPRINT_HEADER_NAME: fingerprintId,
121
+ [fingerprintShared.FINGERPRINT_HEADER_NAME]: fingerprintId,
122
122
  };
123
123
  });
124
124
  }
@@ -134,7 +134,7 @@ function useFingerprintHeaders() {
134
134
  function createFingerprintFetch() {
135
135
  return (url, init) => tslib_es6.__awaiter(this, void 0, void 0, function* () {
136
136
  const fingerprintHeaders = yield createFingerprintHeaders();
137
- const headers = Object.assign(Object.assign({}, fingerprintHeaders), ((init === null || init === void 0 ? void 0 : init.headers) || {}));
137
+ const headers = Object.assign(Object.assign(Object.assign({}, fingerprintHeaders), { [fingerprintShared.FINGERPRINT_SOURCE_REFER]: document.referrer || '' }), ((init === null || init === void 0 ? void 0 : init.headers) || {}));
138
138
  return fetch(url, Object.assign(Object.assign({}, init), { headers }));
139
139
  });
140
140
  }
@@ -1,6 +1,6 @@
1
1
  import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
2
2
  import index from '../../node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.mjs';
3
- import { isValidFingerprintId, FINGERPRINT_STORAGE_KEY, FINGERPRINT_COOKIE_NAME } from './fingerprint-shared.mjs';
3
+ import { isValidFingerprintId, FINGERPRINT_STORAGE_KEY, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_SOURCE_REFER } from './fingerprint-shared.mjs';
4
4
 
5
5
  /**
6
6
  * Fingerprint Client Utilities
@@ -116,7 +116,7 @@ function createFingerprintHeaders() {
116
116
  return __awaiter(this, void 0, void 0, function* () {
117
117
  const fingerprintId = yield getOrGenerateFingerprintId();
118
118
  return {
119
- FINGERPRINT_HEADER_NAME: fingerprintId,
119
+ [FINGERPRINT_HEADER_NAME]: fingerprintId,
120
120
  };
121
121
  });
122
122
  }
@@ -132,7 +132,7 @@ function useFingerprintHeaders() {
132
132
  function createFingerprintFetch() {
133
133
  return (url, init) => __awaiter(this, void 0, void 0, function* () {
134
134
  const fingerprintHeaders = yield createFingerprintHeaders();
135
- const headers = Object.assign(Object.assign({}, fingerprintHeaders), ((init === null || init === void 0 ? void 0 : init.headers) || {}));
135
+ const headers = Object.assign(Object.assign(Object.assign({}, fingerprintHeaders), { [FINGERPRINT_SOURCE_REFER]: document.referrer || '' }), ((init === null || init === void 0 ? void 0 : init.headers) || {}));
136
136
  return fetch(url, Object.assign(Object.assign({}, init), { headers }));
137
137
  });
138
138
  }
@@ -5,6 +5,7 @@
5
5
  export declare const FINGERPRINT_STORAGE_KEY = "__x_fingerprint_id";
6
6
  export declare const FINGERPRINT_HEADER_NAME = "x-fingerprint-id-v8";
7
7
  export declare const FINGERPRINT_COOKIE_NAME = "__x_fingerprint_id";
8
+ export declare const FINGERPRINT_SOURCE_REFER = "x-source-ref";
8
9
  /**
9
10
  * 验证fingerprint ID格式
10
11
  * 可以在客户端和服务端使用
@@ -8,6 +8,7 @@
8
8
  const FINGERPRINT_STORAGE_KEY = '__x_fingerprint_id';
9
9
  const FINGERPRINT_HEADER_NAME = 'x-fingerprint-id-v8';
10
10
  const FINGERPRINT_COOKIE_NAME = '__x_fingerprint_id';
11
+ const FINGERPRINT_SOURCE_REFER = 'x-source-ref';
11
12
  /**
12
13
  * 验证fingerprint ID格式
13
14
  * 可以在客户端和服务端使用
@@ -31,5 +32,6 @@ const FINGERPRINT_CONSTANTS = {
31
32
  exports.FINGERPRINT_CONSTANTS = FINGERPRINT_CONSTANTS;
32
33
  exports.FINGERPRINT_COOKIE_NAME = FINGERPRINT_COOKIE_NAME;
33
34
  exports.FINGERPRINT_HEADER_NAME = FINGERPRINT_HEADER_NAME;
35
+ exports.FINGERPRINT_SOURCE_REFER = FINGERPRINT_SOURCE_REFER;
34
36
  exports.FINGERPRINT_STORAGE_KEY = FINGERPRINT_STORAGE_KEY;
35
37
  exports.isValidFingerprintId = isValidFingerprintId;
@@ -6,6 +6,7 @@
6
6
  const FINGERPRINT_STORAGE_KEY = '__x_fingerprint_id';
7
7
  const FINGERPRINT_HEADER_NAME = 'x-fingerprint-id-v8';
8
8
  const FINGERPRINT_COOKIE_NAME = '__x_fingerprint_id';
9
+ const FINGERPRINT_SOURCE_REFER = 'x-source-ref';
9
10
  /**
10
11
  * 验证fingerprint ID格式
11
12
  * 可以在客户端和服务端使用
@@ -26,4 +27,4 @@ const FINGERPRINT_CONSTANTS = {
26
27
  COOKIE_NAME: FINGERPRINT_COOKIE_NAME,
27
28
  };
28
29
 
29
- export { FINGERPRINT_CONSTANTS, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_STORAGE_KEY, isValidFingerprintId };
30
+ export { FINGERPRINT_CONSTANTS, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_SOURCE_REFER, FINGERPRINT_STORAGE_KEY, isValidFingerprintId };
@@ -11,6 +11,7 @@ var fingerprintProvider = require('./fingerprint-provider.js');
11
11
  exports.FINGERPRINT_CONSTANTS = fingerprintShared.FINGERPRINT_CONSTANTS;
12
12
  exports.FINGERPRINT_COOKIE_NAME = fingerprintShared.FINGERPRINT_COOKIE_NAME;
13
13
  exports.FINGERPRINT_HEADER_NAME = fingerprintShared.FINGERPRINT_HEADER_NAME;
14
+ exports.FINGERPRINT_SOURCE_REFER = fingerprintShared.FINGERPRINT_SOURCE_REFER;
14
15
  exports.FINGERPRINT_STORAGE_KEY = fingerprintShared.FINGERPRINT_STORAGE_KEY;
15
16
  exports.isValidFingerprintId = fingerprintShared.isValidFingerprintId;
16
17
  exports.clearFingerprintId = fingerprintClient.clearFingerprintId;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- export { FINGERPRINT_CONSTANTS, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_STORAGE_KEY, isValidFingerprintId } from './fingerprint-shared.mjs';
2
+ export { FINGERPRINT_CONSTANTS, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_SOURCE_REFER, FINGERPRINT_STORAGE_KEY, isValidFingerprintId } from './fingerprint-shared.mjs';
3
3
  export { clearFingerprintId, createFingerprintFetch, createFingerprintHeaders, generateFingerprintId, getFingerprintId, getOrGenerateFingerprintId, setFingerprintId, useFingerprintHeaders } from './fingerprint-client.mjs';
4
4
  export { useFingerprint } from './use-fingerprint.mjs';
5
5
  export { FingerprintProvider, FingerprintStatus, useFingerprintContext, useFingerprintContextSafe, withFingerprint } from './fingerprint-provider.mjs';
@@ -8,6 +8,7 @@ var fingerprintServer = require('./fingerprint-server.js');
8
8
  exports.FINGERPRINT_CONSTANTS = fingerprintShared.FINGERPRINT_CONSTANTS;
9
9
  exports.FINGERPRINT_COOKIE_NAME = fingerprintShared.FINGERPRINT_COOKIE_NAME;
10
10
  exports.FINGERPRINT_HEADER_NAME = fingerprintShared.FINGERPRINT_HEADER_NAME;
11
+ exports.FINGERPRINT_SOURCE_REFER = fingerprintShared.FINGERPRINT_SOURCE_REFER;
11
12
  exports.FINGERPRINT_STORAGE_KEY = fingerprintShared.FINGERPRINT_STORAGE_KEY;
12
13
  exports.isValidFingerprintId = fingerprintShared.isValidFingerprintId;
13
14
  exports.extractFingerprintFromNextRequest = fingerprintServer.extractFingerprintFromNextRequest;
@@ -1,2 +1,2 @@
1
- export { FINGERPRINT_CONSTANTS, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_STORAGE_KEY, isValidFingerprintId } from './fingerprint-shared.mjs';
1
+ export { FINGERPRINT_CONSTANTS, FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_SOURCE_REFER, FINGERPRINT_STORAGE_KEY, isValidFingerprintId } from './fingerprint-shared.mjs';
2
2
  export { extractFingerprintFromNextRequest, extractFingerprintFromNextStores, extractFingerprintId, generateServerFingerprintId } from './fingerprint-server.mjs';
@@ -4,6 +4,7 @@
4
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.3/node_modules/tslib/tslib.es6.js');
5
5
  var React = require('react');
6
6
  var fingerprintClient = require('./fingerprint-client.js');
7
+ var fingerprintShared = require('./fingerprint-shared.js');
7
8
 
8
9
  /**
9
10
  * Hook for managing fingerprint ID and anonymous user data
@@ -64,7 +65,7 @@ function useFingerprint(config) {
64
65
  const fingerprintHeaders = yield fingerprintClient.createFingerprintHeaders();
65
66
  const response = yield fetch(config.apiEndpoint, {
66
67
  method: 'POST',
67
- headers: Object.assign({ 'Content-Type': 'application/json' }, fingerprintHeaders),
68
+ headers: Object.assign({ 'Content-Type': 'application/json', [fingerprintShared.FINGERPRINT_SOURCE_REFER]: document.referrer || '' }, fingerprintHeaders),
68
69
  body: JSON.stringify({ fingerprintId }),
69
70
  });
70
71
  if (!response.ok) {
@@ -2,6 +2,7 @@
2
2
  import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
3
3
  import { useState, useCallback, useEffect } from 'react';
4
4
  import { getOrGenerateFingerprintId, createFingerprintHeaders } from './fingerprint-client.mjs';
5
+ import { FINGERPRINT_SOURCE_REFER } from './fingerprint-shared.mjs';
5
6
 
6
7
  /**
7
8
  * Hook for managing fingerprint ID and anonymous user data
@@ -62,7 +63,7 @@ function useFingerprint(config) {
62
63
  const fingerprintHeaders = yield createFingerprintHeaders();
63
64
  const response = yield fetch(config.apiEndpoint, {
64
65
  method: 'POST',
65
- headers: Object.assign({ 'Content-Type': 'application/json' }, fingerprintHeaders),
66
+ headers: Object.assign({ 'Content-Type': 'application/json', [FINGERPRINT_SOURCE_REFER]: document.referrer || '' }, fingerprintHeaders),
66
67
  body: JSON.stringify({ fingerprintId }),
67
68
  });
68
69
  if (!response.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/third-ui",
3
- "version": "10.1.2",
3
+ "version": "11.0.0",
4
4
  "description": "Third-party integrated UI components for windrun-huaiin projects",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -81,8 +81,8 @@
81
81
  "react-medium-image-zoom": "^5.2.14",
82
82
  "swiper": "^12.0.3",
83
83
  "zod": "^4.1.12",
84
- "@windrun-huaiin/base-ui": "^10.1.0",
85
- "@windrun-huaiin/lib": "^10.1.0"
84
+ "@windrun-huaiin/base-ui": "^11.0.0",
85
+ "@windrun-huaiin/lib": "^11.0.0"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "clsx": "^2.1.1",
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import FingerprintJS from '@fingerprintjs/fingerprintjs';
8
- import { FINGERPRINT_STORAGE_KEY, FINGERPRINT_COOKIE_NAME, isValidFingerprintId } from './fingerprint-shared';
8
+ import { FINGERPRINT_COOKIE_NAME, FINGERPRINT_HEADER_NAME, FINGERPRINT_SOURCE_REFER, FINGERPRINT_STORAGE_KEY, isValidFingerprintId } from './fingerprint-shared';
9
9
 
10
10
  /**
11
11
  * 检查浏览器存储(localStorage 和 cookie)中的指纹 ID
@@ -128,7 +128,7 @@ export async function getOrGenerateFingerprintId(): Promise<string> {
128
128
  export async function createFingerprintHeaders(): Promise<Record<string, string>> {
129
129
  const fingerprintId = await getOrGenerateFingerprintId();
130
130
  return {
131
- FINGERPRINT_HEADER_NAME: fingerprintId,
131
+ [FINGERPRINT_HEADER_NAME]: fingerprintId,
132
132
  };
133
133
  }
134
134
 
@@ -147,6 +147,7 @@ export function createFingerprintFetch() {
147
147
  const fingerprintHeaders = await createFingerprintHeaders();
148
148
  const headers = {
149
149
  ...fingerprintHeaders,
150
+ [FINGERPRINT_SOURCE_REFER]: document.referrer || '',
150
151
  ...(init?.headers || {}),
151
152
  };
152
153
 
@@ -7,6 +7,7 @@
7
7
  export const FINGERPRINT_STORAGE_KEY = '__x_fingerprint_id';
8
8
  export const FINGERPRINT_HEADER_NAME = 'x-fingerprint-id-v8';
9
9
  export const FINGERPRINT_COOKIE_NAME = '__x_fingerprint_id';
10
+ export const FINGERPRINT_SOURCE_REFER = 'x-source-ref';
10
11
 
11
12
  /**
12
13
  * 验证fingerprint ID格式
@@ -12,6 +12,7 @@ import type {
12
12
  XSubscription,
13
13
  XUser
14
14
  } from './types';
15
+ import { FINGERPRINT_SOURCE_REFER } from './fingerprint-shared'
15
16
 
16
17
  /**
17
18
  * Hook for managing fingerprint ID and anonymous user data
@@ -77,6 +78,7 @@ export function useFingerprint(config: FingerprintConfig): UseFingerprintResult
77
78
  method: 'POST',
78
79
  headers: {
79
80
  'Content-Type': 'application/json',
81
+ [FINGERPRINT_SOURCE_REFER]: document.referrer || '',
80
82
  ...fingerprintHeaders,
81
83
  },
82
84
  body: JSON.stringify({ fingerprintId }),
@@ -1,18 +0,0 @@
1
- interface GalleryItem {
2
- id: string;
3
- url: string;
4
- altMsg: string;
5
- }
6
- interface GalleryData {
7
- titleL: string;
8
- eyesOn: string;
9
- titleR: string;
10
- description: string;
11
- items: GalleryItem[];
12
- defaultImgUrl: string;
13
- downloadPrefix: string;
14
- }
15
- export declare function GalleryInteractive({ data }: {
16
- data: GalleryData;
17
- }): null;
18
- export {};
@@ -1,138 +0,0 @@
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.3/node_modules/tslib/tslib.es6.js');
5
- var React = require('react');
6
-
7
- function GalleryInteractive({ data }) {
8
- const [imageErrors, setImageErrors] = React.useState(new Set());
9
- const [downloadingItems, setDownloadingItems] = React.useState(new Set());
10
- // Get CDN proxy URL from environment
11
- const cdnProxyUrl = process.env.NEXT_PUBLIC_STYLE_CDN_PROXY_URL;
12
- React.useEffect(() => {
13
- // Progressive enhancement: Add download functionality and error handling
14
- data.items.forEach((item, index) => {
15
- const downloadButton = document.querySelector(`[data-gallery-download="${item.id}"]`);
16
- const imageElement = document.querySelector(`[data-gallery-image="${item.id}"]`);
17
- if (downloadButton) {
18
- const handleDownload = () => tslib_es6.__awaiter(this, void 0, void 0, function* () {
19
- var _a;
20
- // Prevent duplicate clicks
21
- if (downloadingItems.has(item.id)) {
22
- return;
23
- }
24
- // Set download status
25
- setDownloadingItems(prev => new Set(prev).add(item.id));
26
- try {
27
- if (!cdnProxyUrl) {
28
- throw new Error('CDN proxy URL not configured');
29
- }
30
- // Use R2 proxy to download directly
31
- const originalUrl = new URL(item.url);
32
- const filename = originalUrl.pathname.substring(1);
33
- // Build proxy download URL
34
- const proxyUrl = `${cdnProxyUrl}/${encodeURIComponent(filename)}`;
35
- // Extract file extension from URL
36
- const urlExtension = (_a = item.url.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
37
- let extension = '.webp';
38
- if (urlExtension && ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(urlExtension)) {
39
- extension = `.${urlExtension}`;
40
- }
41
- // Fetch file from proxy
42
- const response = yield fetch(proxyUrl);
43
- if (!response.ok) {
44
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
45
- }
46
- // Convert to blob
47
- const blob = yield response.blob();
48
- const blobUrl = URL.createObjectURL(blob);
49
- // Create download link and trigger download
50
- const a = document.createElement('a');
51
- a.href = blobUrl;
52
- a.download = `${data.downloadPrefix}-${index + 1}${extension}`;
53
- a.style.display = 'none';
54
- document.body.appendChild(a);
55
- a.click();
56
- // Clean up DOM elements and blob URL
57
- setTimeout(() => {
58
- document.body.removeChild(a);
59
- URL.revokeObjectURL(blobUrl);
60
- }, 100);
61
- }
62
- catch (error) {
63
- console.error('Download failed:', error);
64
- }
65
- finally {
66
- // Clear download status
67
- setDownloadingItems(prev => {
68
- const newSet = new Set(prev);
69
- newSet.delete(item.id);
70
- return newSet;
71
- });
72
- }
73
- });
74
- downloadButton.addEventListener('click', handleDownload);
75
- }
76
- if (imageElement) {
77
- const handleImageError = () => {
78
- setImageErrors(prev => new Set(prev).add(item.id));
79
- // Update image src to default
80
- imageElement.src = data.defaultImgUrl;
81
- };
82
- imageElement.addEventListener('error', handleImageError);
83
- }
84
- });
85
- // Update download button states based on downloading status
86
- const updateDownloadStates = () => {
87
- data.items.forEach((item) => {
88
- const downloadButton = document.querySelector(`[data-gallery-download="${item.id}"]`);
89
- if (downloadButton) {
90
- const isDownloading = downloadingItems.has(item.id);
91
- if (isDownloading) {
92
- downloadButton.disabled = true;
93
- downloadButton.classList.add('bg-black/30', 'text-white/50');
94
- downloadButton.classList.remove('bg-black/50', 'hover:bg-black/70', 'text-white/80', 'hover:text-white');
95
- // Replace icon with spinner
96
- downloadButton.innerHTML = `
97
- <svg class="h-5 w-5 text-white animate-spin" fill="none" viewBox="0 0 24 24">
98
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
99
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
100
- </svg>
101
- `;
102
- }
103
- else {
104
- downloadButton.disabled = false;
105
- downloadButton.classList.remove('bg-black/30', 'text-white/50');
106
- downloadButton.classList.add('bg-black/50', 'hover:bg-black/70', 'text-white/80', 'hover:text-white');
107
- // Reset to download icon
108
- downloadButton.innerHTML = `
109
- <svg class="h-5 w-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
110
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
111
- </svg>
112
- `;
113
- }
114
- }
115
- });
116
- };
117
- updateDownloadStates();
118
- // Cleanup event listeners
119
- return () => {
120
- data.items.forEach((item) => {
121
- var _a, _b;
122
- const downloadButton = document.querySelector(`[data-gallery-download="${item.id}"]`);
123
- const imageElement = document.querySelector(`[data-gallery-image="${item.id}"]`);
124
- if (downloadButton) {
125
- const newButton = downloadButton.cloneNode(true);
126
- (_a = downloadButton.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(newButton, downloadButton);
127
- }
128
- if (imageElement) {
129
- const newImage = imageElement.cloneNode(true);
130
- (_b = imageElement.parentNode) === null || _b === void 0 ? void 0 : _b.replaceChild(newImage, imageElement);
131
- }
132
- });
133
- };
134
- }, [data, downloadingItems, imageErrors, cdnProxyUrl]);
135
- return null; // Progressive enhancement - no additional DOM rendering
136
- }
137
-
138
- exports.GalleryInteractive = GalleryInteractive;
@@ -1,136 +0,0 @@
1
- "use client";
2
- import { __awaiter } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
3
- import { useState, useEffect } from 'react';
4
-
5
- function GalleryInteractive({ data }) {
6
- const [imageErrors, setImageErrors] = useState(new Set());
7
- const [downloadingItems, setDownloadingItems] = useState(new Set());
8
- // Get CDN proxy URL from environment
9
- const cdnProxyUrl = process.env.NEXT_PUBLIC_STYLE_CDN_PROXY_URL;
10
- useEffect(() => {
11
- // Progressive enhancement: Add download functionality and error handling
12
- data.items.forEach((item, index) => {
13
- const downloadButton = document.querySelector(`[data-gallery-download="${item.id}"]`);
14
- const imageElement = document.querySelector(`[data-gallery-image="${item.id}"]`);
15
- if (downloadButton) {
16
- const handleDownload = () => __awaiter(this, void 0, void 0, function* () {
17
- var _a;
18
- // Prevent duplicate clicks
19
- if (downloadingItems.has(item.id)) {
20
- return;
21
- }
22
- // Set download status
23
- setDownloadingItems(prev => new Set(prev).add(item.id));
24
- try {
25
- if (!cdnProxyUrl) {
26
- throw new Error('CDN proxy URL not configured');
27
- }
28
- // Use R2 proxy to download directly
29
- const originalUrl = new URL(item.url);
30
- const filename = originalUrl.pathname.substring(1);
31
- // Build proxy download URL
32
- const proxyUrl = `${cdnProxyUrl}/${encodeURIComponent(filename)}`;
33
- // Extract file extension from URL
34
- const urlExtension = (_a = item.url.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
35
- let extension = '.webp';
36
- if (urlExtension && ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(urlExtension)) {
37
- extension = `.${urlExtension}`;
38
- }
39
- // Fetch file from proxy
40
- const response = yield fetch(proxyUrl);
41
- if (!response.ok) {
42
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
43
- }
44
- // Convert to blob
45
- const blob = yield response.blob();
46
- const blobUrl = URL.createObjectURL(blob);
47
- // Create download link and trigger download
48
- const a = document.createElement('a');
49
- a.href = blobUrl;
50
- a.download = `${data.downloadPrefix}-${index + 1}${extension}`;
51
- a.style.display = 'none';
52
- document.body.appendChild(a);
53
- a.click();
54
- // Clean up DOM elements and blob URL
55
- setTimeout(() => {
56
- document.body.removeChild(a);
57
- URL.revokeObjectURL(blobUrl);
58
- }, 100);
59
- }
60
- catch (error) {
61
- console.error('Download failed:', error);
62
- }
63
- finally {
64
- // Clear download status
65
- setDownloadingItems(prev => {
66
- const newSet = new Set(prev);
67
- newSet.delete(item.id);
68
- return newSet;
69
- });
70
- }
71
- });
72
- downloadButton.addEventListener('click', handleDownload);
73
- }
74
- if (imageElement) {
75
- const handleImageError = () => {
76
- setImageErrors(prev => new Set(prev).add(item.id));
77
- // Update image src to default
78
- imageElement.src = data.defaultImgUrl;
79
- };
80
- imageElement.addEventListener('error', handleImageError);
81
- }
82
- });
83
- // Update download button states based on downloading status
84
- const updateDownloadStates = () => {
85
- data.items.forEach((item) => {
86
- const downloadButton = document.querySelector(`[data-gallery-download="${item.id}"]`);
87
- if (downloadButton) {
88
- const isDownloading = downloadingItems.has(item.id);
89
- if (isDownloading) {
90
- downloadButton.disabled = true;
91
- downloadButton.classList.add('bg-black/30', 'text-white/50');
92
- downloadButton.classList.remove('bg-black/50', 'hover:bg-black/70', 'text-white/80', 'hover:text-white');
93
- // Replace icon with spinner
94
- downloadButton.innerHTML = `
95
- <svg class="h-5 w-5 text-white animate-spin" fill="none" viewBox="0 0 24 24">
96
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
97
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
98
- </svg>
99
- `;
100
- }
101
- else {
102
- downloadButton.disabled = false;
103
- downloadButton.classList.remove('bg-black/30', 'text-white/50');
104
- downloadButton.classList.add('bg-black/50', 'hover:bg-black/70', 'text-white/80', 'hover:text-white');
105
- // Reset to download icon
106
- downloadButton.innerHTML = `
107
- <svg class="h-5 w-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
108
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
109
- </svg>
110
- `;
111
- }
112
- }
113
- });
114
- };
115
- updateDownloadStates();
116
- // Cleanup event listeners
117
- return () => {
118
- data.items.forEach((item) => {
119
- var _a, _b;
120
- const downloadButton = document.querySelector(`[data-gallery-download="${item.id}"]`);
121
- const imageElement = document.querySelector(`[data-gallery-image="${item.id}"]`);
122
- if (downloadButton) {
123
- const newButton = downloadButton.cloneNode(true);
124
- (_a = downloadButton.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(newButton, downloadButton);
125
- }
126
- if (imageElement) {
127
- const newImage = imageElement.cloneNode(true);
128
- (_b = imageElement.parentNode) === null || _b === void 0 ? void 0 : _b.replaceChild(newImage, imageElement);
129
- }
130
- });
131
- };
132
- }, [data, downloadingItems, imageErrors, cdnProxyUrl]);
133
- return null; // Progressive enhancement - no additional DOM rendering
134
- }
135
-
136
- export { GalleryInteractive };
@@ -1,7 +0,0 @@
1
- interface GalleryProps {
2
- locale: string;
3
- sectionClassName?: string;
4
- button?: React.ReactNode;
5
- }
6
- export declare function Gallery({ locale, sectionClassName, button }: GalleryProps): Promise<import("react/jsx-runtime").JSX.Element>;
7
- export {};
@@ -1,43 +0,0 @@
1
- 'use strict';
2
-
3
- var tslib_es6 = require('../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js');
4
- var jsxRuntime = require('react/jsx-runtime');
5
- var server = require('next-intl/server');
6
- var utils = require('@windrun-huaiin/lib/utils');
7
- var Image = require('next/image');
8
- require('react');
9
- require('@windrun-huaiin/base-ui/components/server');
10
- require('./nprogress-bar.js');
11
- require('@windrun-huaiin/base-ui/ui');
12
- require('./rich-text-expert.js');
13
- require('next/navigation');
14
- var galleryInteractive = require('./gallery-interactive.js');
15
- require('@clerk/nextjs');
16
- require('./money-price/money-price-types.js');
17
- require('next/link');
18
- var swiperReact = require('../node_modules/.pnpm/swiper@12.0.3/node_modules/swiper/swiper-react.js');
19
- var pagination = require('../node_modules/.pnpm/swiper@12.0.3/node_modules/swiper/modules/pagination.js');
20
- var sectionLayout = require('./section-layout.js');
21
-
22
- function Gallery(_a) {
23
- return tslib_es6.__awaiter(this, arguments, void 0, function* ({ locale, sectionClassName, button }) {
24
- const t = yield server.getTranslations({ locale, namespace: 'gallery' });
25
- const galleryItems = t.raw('prompts');
26
- const data = {
27
- titleL: t('titleL'),
28
- eyesOn: t('eyesOn'),
29
- titleR: t('titleR'),
30
- description: t('description'),
31
- items: galleryItems.map((item, index) => ({
32
- id: `gallery-item-${index}`,
33
- url: item.url,
34
- altMsg: item.altMsg
35
- })),
36
- defaultImgUrl: t.raw('defaultImgUrl'),
37
- downloadPrefix: t('downloadPrefix')
38
- };
39
- return (jsxRuntime.jsxs("section", { id: "gallery", className: utils.cn(sectionLayout.responsiveSection, sectionClassName), children: [jsxRuntime.jsxs("h2", { className: "text-3xl md:text-4xl font-bold text-center mb-6", children: [data.titleL, " ", jsxRuntime.jsx("span", { className: "text-purple-500", children: data.eyesOn }), " ", data.titleR] }), jsxRuntime.jsx("p", { className: "text-center max-w-2xl mx-auto mb-16", children: data.description }), jsxRuntime.jsx("div", { className: "block sm:hidden", children: jsxRuntime.jsx(swiperReact.Swiper, { modules: [pagination], pagination: { clickable: true }, spaceBetween: 20, slidesPerView: 1, loop: true, className: "rounded-xl overflow-hidden", children: data.items.map((item, index) => (jsxRuntime.jsx(swiperReact.SwiperSlide, { children: jsxRuntime.jsxs("div", { className: "relative aspect-square bg-gray-100", children: [jsxRuntime.jsx(Image, { src: item.url, alt: item.altMsg, fill: true, className: "object-cover", "data-gallery-image": item.id }), jsxRuntime.jsx("button", { className: "absolute bottom-4 right-4 p-3 rounded-full bg-black/60 hover:bg-black/80 text-white transition-all z-10", "data-gallery-download": item.id, "aria-label": `Download ${item.altMsg}`, children: jsxRuntime.jsx("svg", { className: "h-6 w-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }) })] }) }, item.id))) }) }), jsxRuntime.jsx("div", { className: "hidden sm:grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: data.items.map((item, index) => (jsxRuntime.jsxs("div", { className: "group relative overflow-hidden rounded-xl", "data-gallery-item": item.id, "data-gallery-index": index, children: [jsxRuntime.jsx(Image, { src: item.url, alt: item.altMsg, width: 600, height: 600, className: "w-full h-80 object-cover transition duration-300 group-hover:scale-105", "data-gallery-image": item.id }), jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-end justify-end p-4 opacity-0 group-hover:opacity-100 transition duration-300", children: jsxRuntime.jsx("button", { className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white/80 hover:text-white transition-all duration-300", "data-gallery-download": item.id, "aria-label": `Download ${item.altMsg}`, children: jsxRuntime.jsx("svg", { className: "h-5 w-5 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }) }) })] }, item.id))) }), button && (jsxRuntime.jsx("div", { className: "text-center mt-12", children: button })), jsxRuntime.jsx(galleryInteractive.GalleryInteractive, { data: data })] }));
40
- });
41
- }
42
-
43
- exports.Gallery = Gallery;
@@ -1,41 +0,0 @@
1
- import { __awaiter } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { getTranslations } from 'next-intl/server';
4
- import { cn } from '@windrun-huaiin/lib/utils';
5
- import Image from 'next/image';
6
- import 'react';
7
- import '@windrun-huaiin/base-ui/components/server';
8
- import './nprogress-bar.mjs';
9
- import '@windrun-huaiin/base-ui/ui';
10
- import './rich-text-expert.mjs';
11
- import 'next/navigation';
12
- import { GalleryInteractive } from './gallery-interactive.mjs';
13
- import '@clerk/nextjs';
14
- import './money-price/money-price-types.mjs';
15
- import 'next/link';
16
- import { Swiper, SwiperSlide } from '../node_modules/.pnpm/swiper@12.0.3/node_modules/swiper/swiper-react.mjs';
17
- import Pagination from '../node_modules/.pnpm/swiper@12.0.3/node_modules/swiper/modules/pagination.mjs';
18
- import { responsiveSection } from './section-layout.mjs';
19
-
20
- function Gallery(_a) {
21
- return __awaiter(this, arguments, void 0, function* ({ locale, sectionClassName, button }) {
22
- const t = yield getTranslations({ locale, namespace: 'gallery' });
23
- const galleryItems = t.raw('prompts');
24
- const data = {
25
- titleL: t('titleL'),
26
- eyesOn: t('eyesOn'),
27
- titleR: t('titleR'),
28
- description: t('description'),
29
- items: galleryItems.map((item, index) => ({
30
- id: `gallery-item-${index}`,
31
- url: item.url,
32
- altMsg: item.altMsg
33
- })),
34
- defaultImgUrl: t.raw('defaultImgUrl'),
35
- downloadPrefix: t('downloadPrefix')
36
- };
37
- return (jsxs("section", { id: "gallery", className: cn(responsiveSection, sectionClassName), children: [jsxs("h2", { className: "text-3xl md:text-4xl font-bold text-center mb-6", children: [data.titleL, " ", jsx("span", { className: "text-purple-500", children: data.eyesOn }), " ", data.titleR] }), jsx("p", { className: "text-center max-w-2xl mx-auto mb-16", children: data.description }), jsx("div", { className: "block sm:hidden", children: jsx(Swiper, { modules: [Pagination], pagination: { clickable: true }, spaceBetween: 20, slidesPerView: 1, loop: true, className: "rounded-xl overflow-hidden", children: data.items.map((item, index) => (jsx(SwiperSlide, { children: jsxs("div", { className: "relative aspect-square bg-gray-100", children: [jsx(Image, { src: item.url, alt: item.altMsg, fill: true, className: "object-cover", "data-gallery-image": item.id }), jsx("button", { className: "absolute bottom-4 right-4 p-3 rounded-full bg-black/60 hover:bg-black/80 text-white transition-all z-10", "data-gallery-download": item.id, "aria-label": `Download ${item.altMsg}`, children: jsx("svg", { className: "h-6 w-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }) })] }) }, item.id))) }) }), jsx("div", { className: "hidden sm:grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: data.items.map((item, index) => (jsxs("div", { className: "group relative overflow-hidden rounded-xl", "data-gallery-item": item.id, "data-gallery-index": index, children: [jsx(Image, { src: item.url, alt: item.altMsg, width: 600, height: 600, className: "w-full h-80 object-cover transition duration-300 group-hover:scale-105", "data-gallery-image": item.id }), jsx("div", { className: "absolute inset-0 flex items-end justify-end p-4 opacity-0 group-hover:opacity-100 transition duration-300", children: jsx("button", { className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white/80 hover:text-white transition-all duration-300", "data-gallery-download": item.id, "aria-label": `Download ${item.altMsg}`, children: jsx("svg", { className: "h-5 w-5 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }) }) })] }, item.id))) }), button && (jsx("div", { className: "text-center mt-12", children: button })), jsx(GalleryInteractive, { data: data })] }));
38
- });
39
- }
40
-
41
- export { Gallery };