@windrun-huaiin/third-ui 5.11.4 → 5.12.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/dist/main/index.d.mts +7 -5
- package/dist/main/index.d.ts +7 -5
- package/dist/main/index.js +303 -265
- package/dist/main/index.js.map +1 -1
- package/dist/main/index.mjs +294 -256
- package/dist/main/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/main/gallery.tsx +86 -20
package/package.json
CHANGED
package/src/main/gallery.tsx
CHANGED
|
@@ -1,32 +1,99 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { globalLucideIcons as icons } from "@base-ui/components/global-icon"
|
|
4
|
+
import { cn } from '@lib/utils'
|
|
4
5
|
import { useTranslations } from 'next-intl'
|
|
5
6
|
import Image from "next/image"
|
|
6
|
-
import {
|
|
7
|
-
import { cn } from '@lib/utils';
|
|
7
|
+
import { useState } from 'react'
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface GalleryItem {
|
|
10
|
+
url: string;
|
|
11
|
+
altMsg: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface GalleryProps {
|
|
15
|
+
sectionClassName?: string;
|
|
16
|
+
button?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Gallery({ sectionClassName, button }: GalleryProps) {
|
|
10
20
|
const t = useTranslations('gallery');
|
|
11
|
-
const galleryItems = t.raw('prompts') as
|
|
21
|
+
const galleryItems = t.raw('prompts') as GalleryItem[];
|
|
22
|
+
const defaultImgUrl = t.raw('defaultImgUrl') as string;
|
|
23
|
+
const [imageErrors, setImageErrors] = useState<Set<number>>(new Set());
|
|
12
24
|
|
|
13
|
-
const handleDownload = async (index: number) => {
|
|
25
|
+
const handleDownload = async (item: GalleryItem, index: number) => {
|
|
14
26
|
try {
|
|
15
|
-
|
|
27
|
+
// use fetch to force download, and DO NEED CORS config in R2
|
|
28
|
+
const response = await fetch(item.url, {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
// CORS mode declaration
|
|
31
|
+
mode: 'cors',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
16
38
|
const blob = await response.blob();
|
|
17
39
|
const url = window.URL.createObjectURL(blob);
|
|
40
|
+
|
|
41
|
+
// set extension based on the actual file type
|
|
42
|
+
const contentType = response.headers.get('content-type');
|
|
43
|
+
let extension = '.webp';
|
|
44
|
+
|
|
45
|
+
if (contentType) {
|
|
46
|
+
switch (contentType) {
|
|
47
|
+
case 'image/jpeg':
|
|
48
|
+
case 'image/jpg':
|
|
49
|
+
extension = '.jpg';
|
|
50
|
+
break;
|
|
51
|
+
case 'image/png':
|
|
52
|
+
extension = '.png';
|
|
53
|
+
break;
|
|
54
|
+
case 'image/gif':
|
|
55
|
+
extension = '.gif';
|
|
56
|
+
break;
|
|
57
|
+
case 'image/webp':
|
|
58
|
+
extension = '.webp';
|
|
59
|
+
break;
|
|
60
|
+
case 'image/svg+xml':
|
|
61
|
+
extension = '.svg';
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
// if cannot determine, try to extract the extension from the URL
|
|
65
|
+
const urlExtension = item.url.split('.').pop()?.toLowerCase();
|
|
66
|
+
if (urlExtension && ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(urlExtension)) {
|
|
67
|
+
extension = `.${urlExtension}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const downloadPrefix = t('downloadPrefix');
|
|
18
72
|
const a = document.createElement('a');
|
|
19
73
|
a.href = url;
|
|
20
|
-
a.download =
|
|
74
|
+
a.download = `${downloadPrefix}-${index + 1}${extension}`;
|
|
75
|
+
a.style.display = 'none';
|
|
21
76
|
document.body.appendChild(a);
|
|
22
77
|
a.click();
|
|
23
|
-
|
|
24
|
-
|
|
78
|
+
|
|
79
|
+
// clean up
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
window.URL.revokeObjectURL(url);
|
|
82
|
+
document.body.removeChild(a);
|
|
83
|
+
}, 100);
|
|
25
84
|
} catch (error) {
|
|
26
85
|
console.error('Download failed:', error);
|
|
27
86
|
}
|
|
28
87
|
};
|
|
29
88
|
|
|
89
|
+
const handleImageError = (index: number) => {
|
|
90
|
+
setImageErrors(prev => new Set(prev).add(index));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const getImageSrc = (item: GalleryItem, index: number) => {
|
|
94
|
+
return imageErrors.has(index) ? defaultImgUrl : item.url;
|
|
95
|
+
};
|
|
96
|
+
|
|
30
97
|
return (
|
|
31
98
|
<section id="gallery" className={cn("container mx-auto px-4 py-20 scroll-mt-20", sectionClassName)}>
|
|
32
99
|
<h2 className="text-3xl md:text-4xl font-bold text-center mb-6">
|
|
@@ -36,18 +103,19 @@ export function Gallery({ sectionClassName }: { sectionClassName?: string }) {
|
|
|
36
103
|
{t('description')}
|
|
37
104
|
</p>
|
|
38
105
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
39
|
-
{galleryItems.map((
|
|
106
|
+
{galleryItems.map((item, index) => (
|
|
40
107
|
<div key={index} className="group relative overflow-hidden rounded-xl">
|
|
41
108
|
<Image
|
|
42
|
-
src={
|
|
43
|
-
alt=
|
|
109
|
+
src={getImageSrc(item, index)}
|
|
110
|
+
alt={item.altMsg}
|
|
44
111
|
width={600}
|
|
45
112
|
height={600}
|
|
46
113
|
className="w-full h-80 object-cover transition duration-300 group-hover:scale-105"
|
|
114
|
+
onError={() => handleImageError(index)}
|
|
47
115
|
/>
|
|
48
116
|
<div className="absolute inset-0 flex items-end justify-end p-4 opacity-0 group-hover:opacity-100 transition duration-300">
|
|
49
117
|
<button
|
|
50
|
-
onClick={() => handleDownload(index)}
|
|
118
|
+
onClick={() => handleDownload(item, index)}
|
|
51
119
|
className="bg-black/50 hover:bg-black/70 p-2 rounded-full text-white/80 hover:text-white transition-all duration-300"
|
|
52
120
|
>
|
|
53
121
|
<icons.Download className="h-5 w-5 text-white" />
|
|
@@ -56,13 +124,11 @@ export function Gallery({ sectionClassName }: { sectionClassName?: string }) {
|
|
|
56
124
|
</div>
|
|
57
125
|
))}
|
|
58
126
|
</div>
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/>
|
|
65
|
-
</div>
|
|
127
|
+
{button && (
|
|
128
|
+
<div className="text-center mt-12">
|
|
129
|
+
{button}
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
66
132
|
</section>
|
|
67
133
|
)
|
|
68
134
|
}
|