asterui 0.12.66 → 0.12.67
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/components/Image.js +94 -79
- package/dist/components/Image.js.map +1 -1
- package/package.json +1 -1
package/dist/components/Image.js
CHANGED
|
@@ -1,99 +1,114 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { forwardRef as T, useState as
|
|
3
|
-
const
|
|
1
|
+
import { jsxs as x, Fragment as H, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as T, useState as u, useRef as V, useEffect as B, useCallback as l } from "react";
|
|
3
|
+
const q = "btn", A = "btn-circle", G = "btn-sm", I = T(
|
|
4
4
|
({
|
|
5
|
-
src:
|
|
6
|
-
alt:
|
|
7
|
-
fallback:
|
|
8
|
-
placeholder:
|
|
9
|
-
preview:
|
|
10
|
-
width:
|
|
11
|
-
height:
|
|
12
|
-
className:
|
|
13
|
-
onLoad:
|
|
14
|
-
onError:
|
|
5
|
+
src: m,
|
|
6
|
+
alt: c = "",
|
|
7
|
+
fallback: o,
|
|
8
|
+
placeholder: $,
|
|
9
|
+
preview: d = !0,
|
|
10
|
+
width: f,
|
|
11
|
+
height: h,
|
|
12
|
+
className: S = "",
|
|
13
|
+
onLoad: C,
|
|
14
|
+
onError: N,
|
|
15
15
|
"data-testid": r = "image",
|
|
16
|
-
...
|
|
17
|
-
},
|
|
18
|
-
const [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}, [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
...D
|
|
17
|
+
}, g) => {
|
|
18
|
+
const [a, i] = u(!0), [s, n] = u(!1), [E, p] = u(!1), [j, b] = u(m), [v, w] = u(!1), L = V(null);
|
|
19
|
+
B(() => {
|
|
20
|
+
i(!0), n(!1), w(!1), b(m);
|
|
21
|
+
}, [m]), B(() => {
|
|
22
|
+
const e = L.current;
|
|
23
|
+
if (!(!e || !e.complete)) {
|
|
24
|
+
if (e.naturalWidth > 0) {
|
|
25
|
+
i(!1), n(!1);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (o && !v) {
|
|
29
|
+
w(!0), b(o), i(!0), n(!1);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
i(!1), n(!0);
|
|
33
|
+
}
|
|
34
|
+
}, [j, o, v]);
|
|
35
|
+
const F = l(() => {
|
|
36
|
+
i(!1), n(!1), C?.();
|
|
37
|
+
}, [C]), K = l(() => {
|
|
38
|
+
if (N?.(), o && !v) {
|
|
39
|
+
w(!0), b(o), i(!0), n(!1);
|
|
27
40
|
return;
|
|
28
41
|
}
|
|
29
|
-
|
|
30
|
-
}, [
|
|
31
|
-
|
|
32
|
-
}, [
|
|
33
|
-
|
|
34
|
-
}, []),
|
|
42
|
+
i(!1), n(!0);
|
|
43
|
+
}, [o, v, N]), M = l(() => {
|
|
44
|
+
d && !s && !a && p(!0);
|
|
45
|
+
}, [d, s, a]), P = l(() => {
|
|
46
|
+
p(!1);
|
|
47
|
+
}, []), R = l(
|
|
35
48
|
(e) => {
|
|
36
|
-
|
|
49
|
+
d && !s && !a && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), p(!0));
|
|
37
50
|
},
|
|
38
|
-
[
|
|
39
|
-
),
|
|
40
|
-
e.key === "Escape" &&
|
|
41
|
-
}, []),
|
|
51
|
+
[d, s, a]
|
|
52
|
+
), W = l((e) => {
|
|
53
|
+
e.key === "Escape" && p(!1);
|
|
54
|
+
}, []), k = () => {
|
|
42
55
|
const e = {};
|
|
43
|
-
return
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
return f && (e.width = typeof f == "number" ? `${f}px` : f), h && (e.height = typeof h == "number" ? `${h}px` : h), e;
|
|
57
|
+
}, y = d && !s && !a, z = [
|
|
58
|
+
S,
|
|
59
|
+
y ? "cursor-pointer hover:opacity-80 transition-opacity" : ""
|
|
47
60
|
].filter(Boolean).join(" ");
|
|
48
|
-
return /* @__PURE__ */
|
|
49
|
-
/* @__PURE__ */
|
|
61
|
+
return /* @__PURE__ */ x(H, { children: [
|
|
62
|
+
/* @__PURE__ */ x(
|
|
50
63
|
"div",
|
|
51
64
|
{
|
|
52
65
|
className: "relative inline-block",
|
|
53
|
-
style:
|
|
66
|
+
style: k(),
|
|
54
67
|
"data-testid": r,
|
|
55
|
-
"data-state":
|
|
68
|
+
"data-state": a ? "loading" : s ? "error" : "loaded",
|
|
56
69
|
children: [
|
|
57
|
-
|
|
70
|
+
a && $ && /* @__PURE__ */ t(
|
|
58
71
|
"div",
|
|
59
72
|
{
|
|
60
73
|
className: "absolute inset-0 flex items-center justify-center bg-base-200",
|
|
61
74
|
"data-testid": `${r}-placeholder`,
|
|
62
|
-
children:
|
|
75
|
+
children: $
|
|
63
76
|
}
|
|
64
77
|
),
|
|
65
|
-
/* @__PURE__ */
|
|
78
|
+
/* @__PURE__ */ t(
|
|
66
79
|
"img",
|
|
67
80
|
{
|
|
68
|
-
ref:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
ref: (e) => {
|
|
82
|
+
L.current = e, typeof g == "function" ? g(e) : g && (g.current = e);
|
|
83
|
+
},
|
|
84
|
+
...D,
|
|
85
|
+
src: j,
|
|
86
|
+
alt: c,
|
|
72
87
|
className: z,
|
|
73
88
|
style: {
|
|
74
|
-
...
|
|
75
|
-
opacity:
|
|
89
|
+
...k(),
|
|
90
|
+
opacity: a ? 0 : 1,
|
|
76
91
|
transition: "opacity 150ms ease"
|
|
77
92
|
},
|
|
78
|
-
onLoad:
|
|
79
|
-
onError:
|
|
80
|
-
onClick:
|
|
81
|
-
onKeyDown:
|
|
82
|
-
tabIndex:
|
|
83
|
-
role:
|
|
84
|
-
"aria-label":
|
|
93
|
+
onLoad: F,
|
|
94
|
+
onError: K,
|
|
95
|
+
onClick: M,
|
|
96
|
+
onKeyDown: R,
|
|
97
|
+
tabIndex: y ? 0 : void 0,
|
|
98
|
+
role: y ? "button" : void 0,
|
|
99
|
+
"aria-label": y ? `${c || "Image"} (click to preview)` : void 0,
|
|
85
100
|
"data-testid": `${r}-img`
|
|
86
101
|
}
|
|
87
102
|
),
|
|
88
|
-
|
|
103
|
+
s && /* @__PURE__ */ t(
|
|
89
104
|
"div",
|
|
90
105
|
{
|
|
91
106
|
className: "flex items-center justify-center bg-base-200 text-base-content/50",
|
|
92
|
-
style:
|
|
107
|
+
style: k(),
|
|
93
108
|
"data-testid": `${r}-error`,
|
|
94
109
|
role: "img",
|
|
95
|
-
"aria-label": `Failed to load: ${
|
|
96
|
-
children: /* @__PURE__ */
|
|
110
|
+
"aria-label": `Failed to load: ${c || "image"}`,
|
|
111
|
+
children: /* @__PURE__ */ t(
|
|
97
112
|
"svg",
|
|
98
113
|
{
|
|
99
114
|
className: "w-12 h-12",
|
|
@@ -101,7 +116,7 @@ const R = "btn", V = "btn-circle", q = "btn-sm", A = T(
|
|
|
101
116
|
stroke: "currentColor",
|
|
102
117
|
viewBox: "0 0 24 24",
|
|
103
118
|
"aria-hidden": "true",
|
|
104
|
-
children: /* @__PURE__ */
|
|
119
|
+
children: /* @__PURE__ */ t(
|
|
105
120
|
"path",
|
|
106
121
|
{
|
|
107
122
|
strokeLinecap: "round",
|
|
@@ -117,26 +132,26 @@ const R = "btn", V = "btn-circle", q = "btn-sm", A = T(
|
|
|
117
132
|
]
|
|
118
133
|
}
|
|
119
134
|
),
|
|
120
|
-
|
|
135
|
+
E && /* @__PURE__ */ t(
|
|
121
136
|
"div",
|
|
122
137
|
{
|
|
123
138
|
className: "fixed inset-0 z-50 bg-black bg-opacity-80 flex items-center justify-center p-4",
|
|
124
|
-
onClick:
|
|
125
|
-
onKeyDown:
|
|
139
|
+
onClick: P,
|
|
140
|
+
onKeyDown: W,
|
|
126
141
|
role: "dialog",
|
|
127
142
|
"aria-modal": "true",
|
|
128
|
-
"aria-label": `Preview: ${
|
|
143
|
+
"aria-label": `Preview: ${c || "Image"}`,
|
|
129
144
|
"data-testid": `${r}-preview`,
|
|
130
|
-
children: /* @__PURE__ */
|
|
131
|
-
/* @__PURE__ */
|
|
145
|
+
children: /* @__PURE__ */ x("div", { className: "relative max-w-full max-h-full", children: [
|
|
146
|
+
/* @__PURE__ */ t(
|
|
132
147
|
"button",
|
|
133
148
|
{
|
|
134
|
-
className: `absolute top-4 right-4 ${
|
|
135
|
-
onClick:
|
|
149
|
+
className: `absolute top-4 right-4 ${q} ${A} ${G}`,
|
|
150
|
+
onClick: P,
|
|
136
151
|
"aria-label": "Close preview",
|
|
137
152
|
autoFocus: !0,
|
|
138
153
|
"data-testid": `${r}-preview-close`,
|
|
139
|
-
children: /* @__PURE__ */
|
|
154
|
+
children: /* @__PURE__ */ t(
|
|
140
155
|
"svg",
|
|
141
156
|
{
|
|
142
157
|
className: "w-6 h-6",
|
|
@@ -144,7 +159,7 @@ const R = "btn", V = "btn-circle", q = "btn-sm", A = T(
|
|
|
144
159
|
stroke: "currentColor",
|
|
145
160
|
viewBox: "0 0 24 24",
|
|
146
161
|
"aria-hidden": "true",
|
|
147
|
-
children: /* @__PURE__ */
|
|
162
|
+
children: /* @__PURE__ */ t(
|
|
148
163
|
"path",
|
|
149
164
|
{
|
|
150
165
|
strokeLinecap: "round",
|
|
@@ -157,11 +172,11 @@ const R = "btn", V = "btn-circle", q = "btn-sm", A = T(
|
|
|
157
172
|
)
|
|
158
173
|
}
|
|
159
174
|
),
|
|
160
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ t(
|
|
161
176
|
"img",
|
|
162
177
|
{
|
|
163
|
-
src:
|
|
164
|
-
alt:
|
|
178
|
+
src: m,
|
|
179
|
+
alt: c,
|
|
165
180
|
className: "max-w-full max-h-[90vh] object-contain",
|
|
166
181
|
onClick: (e) => e.stopPropagation(),
|
|
167
182
|
"data-testid": `${r}-preview-img`
|
|
@@ -173,8 +188,8 @@ const R = "btn", V = "btn-circle", q = "btn-sm", A = T(
|
|
|
173
188
|
] });
|
|
174
189
|
}
|
|
175
190
|
);
|
|
176
|
-
|
|
191
|
+
I.displayName = "Image";
|
|
177
192
|
export {
|
|
178
|
-
|
|
193
|
+
I as Image
|
|
179
194
|
};
|
|
180
195
|
//# sourceMappingURL=Image.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Image.js","sources":["../../src/components/Image.tsx"],"sourcesContent":["import React, { useState, useEffect, forwardRef, useCallback } from 'react'\n\n// DaisyUI classes\nconst dBtn = 'btn'\nconst dBtnCircle = 'btn-circle'\nconst dBtnSm = 'btn-sm'\n\nexport interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'placeholder'> {\n /** Image source URL */\n src: string\n /** Alternative text for the image */\n alt?: string\n /** Fallback image URL when source fails to load */\n fallback?: string\n /** Placeholder content shown while loading */\n placeholder?: React.ReactNode\n /** Enable click to preview image in lightbox */\n preview?: boolean\n /** Image width */\n width?: string | number\n /** Image height */\n height?: string | number\n /** Additional CSS classes */\n className?: string\n /** Callback when image loads successfully */\n onLoad?: () => void\n /** Callback when image fails to load */\n onError?: () => void\n /** Test ID for the component */\n 'data-testid'?: string\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(\n (\n {\n src,\n alt = '',\n fallback,\n placeholder,\n preview = true,\n width,\n height,\n className = '',\n onLoad,\n onError,\n 'data-testid': testId = 'image',\n ...props\n },\n ref\n ) => {\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState(false)\n const [showPreview, setShowPreview] = useState(false)\n const [currentSrc, setCurrentSrc] = useState(src)\n const [hasTriedFallback, setHasTriedFallback] = useState(false)\n\n useEffect(() => {\n setLoading(true)\n setError(false)\n setHasTriedFallback(false)\n setCurrentSrc(src)\n }, [src])\n\n const handleLoad = useCallback(() => {\n setLoading(false)\n setError(false)\n onLoad?.()\n }, [onLoad])\n\n const handleError = useCallback(() => {\n onError?.()\n if (fallback && !hasTriedFallback) {\n setHasTriedFallback(true)\n setCurrentSrc(fallback)\n setLoading(true)\n setError(false)\n return\n }\n setLoading(false)\n setError(true)\n }, [fallback, hasTriedFallback, onError])\n\n const handleImageClick = useCallback(() => {\n if (preview && !error && !loading) {\n setShowPreview(true)\n }\n }, [preview, error, loading])\n\n const handleClosePreview = useCallback(() => {\n setShowPreview(false)\n }, [])\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (preview && !error && !loading && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault()\n setShowPreview(true)\n }\n },\n [preview, error, loading]\n )\n\n const handlePreviewKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n setShowPreview(false)\n }\n }, [])\n\n const getStyle = (): React.CSSProperties => {\n const style: React.CSSProperties = {}\n if (width) {\n style.width = typeof width === 'number' ? `${width}px` : width\n }\n if (height) {\n style.height = typeof height === 'number' ? `${height}px` : height\n }\n return style\n }\n\n const isPreviewable = preview && !error && !loading\n const imageClasses = [\n className,\n isPreviewable ? 'cursor-pointer hover:opacity-80 transition-opacity' : '',\n ]\n .filter(Boolean)\n .join(' ')\n\n return (\n <>\n <div\n className=\"relative inline-block\"\n style={getStyle()}\n data-testid={testId}\n data-state={loading ? 'loading' : error ? 'error' : 'loaded'}\n >\n {loading && placeholder && (\n <div\n className=\"absolute inset-0 flex items-center justify-center bg-base-200\"\n data-testid={`${testId}-placeholder`}\n >\n {placeholder}\n </div>\n )}\n <img\n ref={ref}\n {...props}\n src={currentSrc}\n alt={alt}\n className={imageClasses}\n style={{\n ...getStyle(),\n opacity: loading ? 0 : 1,\n transition: 'opacity 150ms ease',\n }}\n onLoad={handleLoad}\n onError={handleError}\n onClick={handleImageClick}\n onKeyDown={handleKeyDown}\n tabIndex={isPreviewable ? 0 : undefined}\n role={isPreviewable ? 'button' : undefined}\n aria-label={isPreviewable ? `${alt || 'Image'} (click to preview)` : undefined}\n data-testid={`${testId}-img`}\n />\n {error && (\n <div\n className=\"flex items-center justify-center bg-base-200 text-base-content/50\"\n style={getStyle()}\n data-testid={`${testId}-error`}\n role=\"img\"\n aria-label={`Failed to load: ${alt || 'image'}`}\n >\n <svg\n className=\"w-12 h-12\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n </div>\n )}\n </div>\n\n {showPreview && (\n <div\n className=\"fixed inset-0 z-50 bg-black bg-opacity-80 flex items-center justify-center p-4\"\n onClick={handleClosePreview}\n onKeyDown={handlePreviewKeyDown}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={`Preview: ${alt || 'Image'}`}\n data-testid={`${testId}-preview`}\n >\n <div className=\"relative max-w-full max-h-full\">\n <button\n className={`absolute top-4 right-4 ${dBtn} ${dBtnCircle} ${dBtnSm}`}\n onClick={handleClosePreview}\n aria-label=\"Close preview\"\n autoFocus\n data-testid={`${testId}-preview-close`}\n >\n <svg\n className=\"w-6 h-6\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n <img\n src={src}\n alt={alt}\n className=\"max-w-full max-h-[90vh] object-contain\"\n onClick={(e) => e.stopPropagation()}\n data-testid={`${testId}-preview-img`}\n />\n </div>\n </div>\n )}\n </>\n )\n }\n)\n\nImage.displayName = 'Image'\n"],"names":["dBtn","dBtnCircle","dBtnSm","Image","forwardRef","src","alt","fallback","placeholder","preview","width","height","className","onLoad","onError","testId","props","ref","loading","setLoading","useState","error","setError","showPreview","setShowPreview","currentSrc","setCurrentSrc","hasTriedFallback","setHasTriedFallback","useEffect","handleLoad","useCallback","handleError","handleImageClick","handleClosePreview","handleKeyDown","handlePreviewKeyDown","getStyle","style","isPreviewable","imageClasses","jsxs","Fragment","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Image.js","sources":["../../src/components/Image.tsx"],"sourcesContent":["import React, { useState, useEffect, forwardRef, useCallback, useRef } from 'react'\n\n// DaisyUI classes\nconst dBtn = 'btn'\nconst dBtnCircle = 'btn-circle'\nconst dBtnSm = 'btn-sm'\n\nexport interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'placeholder'> {\n /** Image source URL */\n src: string\n /** Alternative text for the image */\n alt?: string\n /** Fallback image URL when source fails to load */\n fallback?: string\n /** Placeholder content shown while loading */\n placeholder?: React.ReactNode\n /** Enable click to preview image in lightbox */\n preview?: boolean\n /** Image width */\n width?: string | number\n /** Image height */\n height?: string | number\n /** Additional CSS classes */\n className?: string\n /** Callback when image loads successfully */\n onLoad?: () => void\n /** Callback when image fails to load */\n onError?: () => void\n /** Test ID for the component */\n 'data-testid'?: string\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(\n (\n {\n src,\n alt = '',\n fallback,\n placeholder,\n preview = true,\n width,\n height,\n className = '',\n onLoad,\n onError,\n 'data-testid': testId = 'image',\n ...props\n },\n ref\n ) => {\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState(false)\n const [showPreview, setShowPreview] = useState(false)\n const [currentSrc, setCurrentSrc] = useState(src)\n const [hasTriedFallback, setHasTriedFallback] = useState(false)\n const imgRef = useRef<HTMLImageElement | null>(null)\n\n useEffect(() => {\n setLoading(true)\n setError(false)\n setHasTriedFallback(false)\n setCurrentSrc(src)\n }, [src])\n\n useEffect(() => {\n const img = imgRef.current\n if (!img || !img.complete) return\n\n if (img.naturalWidth > 0) {\n setLoading(false)\n setError(false)\n return\n }\n\n if (fallback && !hasTriedFallback) {\n setHasTriedFallback(true)\n setCurrentSrc(fallback)\n setLoading(true)\n setError(false)\n return\n }\n\n setLoading(false)\n setError(true)\n }, [currentSrc, fallback, hasTriedFallback])\n\n const handleLoad = useCallback(() => {\n setLoading(false)\n setError(false)\n onLoad?.()\n }, [onLoad])\n\n const handleError = useCallback(() => {\n onError?.()\n if (fallback && !hasTriedFallback) {\n setHasTriedFallback(true)\n setCurrentSrc(fallback)\n setLoading(true)\n setError(false)\n return\n }\n setLoading(false)\n setError(true)\n }, [fallback, hasTriedFallback, onError])\n\n const handleImageClick = useCallback(() => {\n if (preview && !error && !loading) {\n setShowPreview(true)\n }\n }, [preview, error, loading])\n\n const handleClosePreview = useCallback(() => {\n setShowPreview(false)\n }, [])\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (preview && !error && !loading && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault()\n setShowPreview(true)\n }\n },\n [preview, error, loading]\n )\n\n const handlePreviewKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n setShowPreview(false)\n }\n }, [])\n\n const getStyle = (): React.CSSProperties => {\n const style: React.CSSProperties = {}\n if (width) {\n style.width = typeof width === 'number' ? `${width}px` : width\n }\n if (height) {\n style.height = typeof height === 'number' ? `${height}px` : height\n }\n return style\n }\n\n const isPreviewable = preview && !error && !loading\n const imageClasses = [\n className,\n isPreviewable ? 'cursor-pointer hover:opacity-80 transition-opacity' : '',\n ]\n .filter(Boolean)\n .join(' ')\n\n return (\n <>\n <div\n className=\"relative inline-block\"\n style={getStyle()}\n data-testid={testId}\n data-state={loading ? 'loading' : error ? 'error' : 'loaded'}\n >\n {loading && placeholder && (\n <div\n className=\"absolute inset-0 flex items-center justify-center bg-base-200\"\n data-testid={`${testId}-placeholder`}\n >\n {placeholder}\n </div>\n )}\n <img\n ref={(node) => {\n imgRef.current = node\n if (typeof ref === 'function') {\n ref(node)\n } else if (ref) {\n ref.current = node\n }\n }}\n {...props}\n src={currentSrc}\n alt={alt}\n className={imageClasses}\n style={{\n ...getStyle(),\n opacity: loading ? 0 : 1,\n transition: 'opacity 150ms ease',\n }}\n onLoad={handleLoad}\n onError={handleError}\n onClick={handleImageClick}\n onKeyDown={handleKeyDown}\n tabIndex={isPreviewable ? 0 : undefined}\n role={isPreviewable ? 'button' : undefined}\n aria-label={isPreviewable ? `${alt || 'Image'} (click to preview)` : undefined}\n data-testid={`${testId}-img`}\n />\n {error && (\n <div\n className=\"flex items-center justify-center bg-base-200 text-base-content/50\"\n style={getStyle()}\n data-testid={`${testId}-error`}\n role=\"img\"\n aria-label={`Failed to load: ${alt || 'image'}`}\n >\n <svg\n className=\"w-12 h-12\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n </div>\n )}\n </div>\n\n {showPreview && (\n <div\n className=\"fixed inset-0 z-50 bg-black bg-opacity-80 flex items-center justify-center p-4\"\n onClick={handleClosePreview}\n onKeyDown={handlePreviewKeyDown}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={`Preview: ${alt || 'Image'}`}\n data-testid={`${testId}-preview`}\n >\n <div className=\"relative max-w-full max-h-full\">\n <button\n className={`absolute top-4 right-4 ${dBtn} ${dBtnCircle} ${dBtnSm}`}\n onClick={handleClosePreview}\n aria-label=\"Close preview\"\n autoFocus\n data-testid={`${testId}-preview-close`}\n >\n <svg\n className=\"w-6 h-6\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n <img\n src={src}\n alt={alt}\n className=\"max-w-full max-h-[90vh] object-contain\"\n onClick={(e) => e.stopPropagation()}\n data-testid={`${testId}-preview-img`}\n />\n </div>\n </div>\n )}\n </>\n )\n }\n)\n\nImage.displayName = 'Image'\n"],"names":["dBtn","dBtnCircle","dBtnSm","Image","forwardRef","src","alt","fallback","placeholder","preview","width","height","className","onLoad","onError","testId","props","ref","loading","setLoading","useState","error","setError","showPreview","setShowPreview","currentSrc","setCurrentSrc","hasTriedFallback","setHasTriedFallback","imgRef","useRef","useEffect","img","handleLoad","useCallback","handleError","handleImageClick","handleClosePreview","handleKeyDown","handlePreviewKeyDown","getStyle","style","isPreviewable","imageClasses","jsxs","Fragment","jsx","node"],"mappings":"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"}
|