@nektarlabs/adsterix-widget 1.0.1 → 1.1.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/README.md +12 -4
- package/dist/index.d.mts +11 -0
- package/dist/index.mjs +305 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,10 +53,18 @@ To display an ad, you need a cast hash of the corresponding Farcaster cast. Foll
|
|
|
53
53
|
|
|
54
54
|
## Props
|
|
55
55
|
|
|
56
|
-
| Prop | Type
|
|
57
|
-
| ---------- |
|
|
58
|
-
| `castHash` | `string`
|
|
59
|
-
| `onClose` | `() => void`
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
| ---------- | ------------------ | -------- | -------------------------------------------------------------------------------- |
|
|
58
|
+
| `castHash` | `string` | — | The Farcaster cast hash of the ad to display |
|
|
59
|
+
| `onClose` | `() => void` | — | Callback fired when the user closes the ad |
|
|
60
|
+
| `width` | `string \| number` | `"100%"` | Width of the widget (px or string) |
|
|
61
|
+
| `height` | `string \| number` | — | Height of the widget (px or string). If not provided, maintains 3:2 aspect ratio |
|
|
62
|
+
|
|
63
|
+
## Example
|
|
64
|
+
|
|
65
|
+
<p align="center">
|
|
66
|
+
<img src="./resources/miniapp.png" alt="Miniapp" width="300"/>
|
|
67
|
+
</p>
|
|
60
68
|
|
|
61
69
|
## License
|
|
62
70
|
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AdsterixWidgetProps {
|
|
4
|
+
castHash?: string;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
width?: string | number;
|
|
7
|
+
height?: string | number;
|
|
8
|
+
}
|
|
9
|
+
declare const AdsterixWidget: React.FC<AdsterixWidgetProps>;
|
|
10
|
+
|
|
11
|
+
export { AdsterixWidget, type AdsterixWidgetProps };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
import { X, ShoppingBag, ExternalLink, Sparkles } from 'lucide-react';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/AdsterixWidget.tsx
|
|
7
|
+
var CtaButton = ({ label, icon, onClick, showLabel }) => /* @__PURE__ */ jsxs(
|
|
8
|
+
motion.div,
|
|
9
|
+
{
|
|
10
|
+
onClick,
|
|
11
|
+
style: {
|
|
12
|
+
display: "flex",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
gap: showLabel ? 6 : 0,
|
|
15
|
+
padding: showLabel ? "8px 14px" : "8px",
|
|
16
|
+
borderRadius: 20,
|
|
17
|
+
background: "rgba(255,255,255,0.9)",
|
|
18
|
+
backdropFilter: "blur(8px)",
|
|
19
|
+
color: "#0f172a",
|
|
20
|
+
fontSize: 13,
|
|
21
|
+
fontWeight: 600,
|
|
22
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
23
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
24
|
+
cursor: "pointer"
|
|
25
|
+
},
|
|
26
|
+
whileHover: {
|
|
27
|
+
y: -2,
|
|
28
|
+
background: "rgba(255,255,255,0.98)",
|
|
29
|
+
boxShadow: "0 8px 20px rgba(0,0,0,0.2)"
|
|
30
|
+
},
|
|
31
|
+
transition: { type: "spring", stiffness: 400, damping: 15 },
|
|
32
|
+
children: [
|
|
33
|
+
showLabel && label,
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
motion.span,
|
|
36
|
+
{
|
|
37
|
+
style: { display: "flex", alignItems: "center" },
|
|
38
|
+
whileHover: { x: showLabel ? 2 : 0 },
|
|
39
|
+
transition: { type: "spring", stiffness: 400, damping: 15 },
|
|
40
|
+
children: icon
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
|
|
47
|
+
const [ctaDetails, setCtaDetails] = React.useState(null);
|
|
48
|
+
const [error, setError] = React.useState(null);
|
|
49
|
+
const [loading, setLoading] = React.useState(false);
|
|
50
|
+
const [imageLoaded, setImageLoaded] = React.useState(false);
|
|
51
|
+
const [visible, setVisible] = React.useState(true);
|
|
52
|
+
const [isSmall, setIsSmall] = React.useState(true);
|
|
53
|
+
const containerRef = React.useRef(null);
|
|
54
|
+
React.useEffect(() => {
|
|
55
|
+
if (!castHash) return;
|
|
56
|
+
const fetchCtaDetails = async () => {
|
|
57
|
+
setLoading(true);
|
|
58
|
+
setError(null);
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch(`https://www.adsterix.xyz/api/ads/cta-details/${castHash}`);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new Error(`Failed to fetch: ${response.status}`);
|
|
63
|
+
}
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
setCtaDetails(data);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
68
|
+
} finally {
|
|
69
|
+
setLoading(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
fetchCtaDetails();
|
|
73
|
+
}, [castHash]);
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
if (!containerRef.current) return;
|
|
76
|
+
const checkSize = (entries) => {
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const width2 = entry.contentRect.width;
|
|
79
|
+
setIsSmall(width2 < 270);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const resizeObserver = new ResizeObserver(checkSize);
|
|
83
|
+
resizeObserver.observe(containerRef.current);
|
|
84
|
+
if (containerRef.current) {
|
|
85
|
+
const width2 = containerRef.current.offsetWidth;
|
|
86
|
+
setIsSmall(width2 < 400);
|
|
87
|
+
}
|
|
88
|
+
return () => resizeObserver.disconnect();
|
|
89
|
+
}, [loading, ctaDetails]);
|
|
90
|
+
const handleAdClick = () => {
|
|
91
|
+
if (ctaDetails == null ? void 0 : ctaDetails.url) {
|
|
92
|
+
window.open(ctaDetails.url, "_blank", "noopener,noreferrer");
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const handleBuySlotClick = (e) => {
|
|
96
|
+
e.stopPropagation();
|
|
97
|
+
if (ctaDetails == null ? void 0 : ctaDetails.buySlotUrl) window.open(ctaDetails.buySlotUrl, "_blank", "noopener,noreferrer");
|
|
98
|
+
};
|
|
99
|
+
const handleClose = (e) => {
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
setVisible(false);
|
|
102
|
+
onClose == null ? void 0 : onClose();
|
|
103
|
+
};
|
|
104
|
+
const containerStyle = {
|
|
105
|
+
position: "relative",
|
|
106
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
107
|
+
...height ? { height: typeof height === "number" ? `${height}px` : height } : { aspectRatio: "3 / 2" },
|
|
108
|
+
borderRadius: 12,
|
|
109
|
+
overflow: "hidden"
|
|
110
|
+
};
|
|
111
|
+
if (!visible) return null;
|
|
112
|
+
if (error) {
|
|
113
|
+
return /* @__PURE__ */ jsx(
|
|
114
|
+
motion.div,
|
|
115
|
+
{
|
|
116
|
+
initial: { opacity: 0, y: 10 },
|
|
117
|
+
animate: { opacity: 1, y: 0 },
|
|
118
|
+
style: {
|
|
119
|
+
...containerStyle,
|
|
120
|
+
display: "flex",
|
|
121
|
+
alignItems: "center",
|
|
122
|
+
justifyContent: "center",
|
|
123
|
+
padding: 16,
|
|
124
|
+
background: "linear-gradient(135deg, #0f172a 0%, #1e293b 100%)",
|
|
125
|
+
color: "#f87171",
|
|
126
|
+
fontSize: 14,
|
|
127
|
+
fontWeight: 500,
|
|
128
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
129
|
+
},
|
|
130
|
+
children: "Unable to load ad"
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (loading || !ctaDetails) {
|
|
135
|
+
return /* @__PURE__ */ jsx(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
ref: containerRef,
|
|
139
|
+
style: {
|
|
140
|
+
...containerStyle,
|
|
141
|
+
background: "linear-gradient(135deg, #0f172a 0%, #1e293b 100%)"
|
|
142
|
+
},
|
|
143
|
+
children: /* @__PURE__ */ jsx(
|
|
144
|
+
motion.div,
|
|
145
|
+
{
|
|
146
|
+
style: {
|
|
147
|
+
position: "absolute",
|
|
148
|
+
inset: 0,
|
|
149
|
+
background: "linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.05) 50%, transparent 100%)"
|
|
150
|
+
},
|
|
151
|
+
animate: { x: ["-100%", "100%"] },
|
|
152
|
+
transition: { duration: 1.5, repeat: Infinity, ease: "linear" }
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return /* @__PURE__ */ jsxs(
|
|
159
|
+
motion.div,
|
|
160
|
+
{
|
|
161
|
+
ref: containerRef,
|
|
162
|
+
onClick: handleAdClick,
|
|
163
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
164
|
+
animate: { opacity: 1, scale: 1 },
|
|
165
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
166
|
+
whileHover: { scale: 1.02 },
|
|
167
|
+
whileTap: { scale: 0.98 },
|
|
168
|
+
transition: { type: "spring", stiffness: 300, damping: 20 },
|
|
169
|
+
style: {
|
|
170
|
+
...containerStyle,
|
|
171
|
+
cursor: "pointer",
|
|
172
|
+
boxShadow: "0 10px 30px rgba(0,0,0,0.2)"
|
|
173
|
+
},
|
|
174
|
+
children: [
|
|
175
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: imageLoaded && /* @__PURE__ */ jsx(
|
|
176
|
+
motion.img,
|
|
177
|
+
{
|
|
178
|
+
src: ctaDetails.image,
|
|
179
|
+
alt: "Advertisement",
|
|
180
|
+
initial: { opacity: 0 },
|
|
181
|
+
animate: { opacity: 1 },
|
|
182
|
+
transition: { duration: 0.4 },
|
|
183
|
+
style: {
|
|
184
|
+
position: "absolute",
|
|
185
|
+
inset: 0,
|
|
186
|
+
width: "100%",
|
|
187
|
+
height: "100%",
|
|
188
|
+
objectFit: "cover"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
) }),
|
|
192
|
+
/* @__PURE__ */ jsx("img", { src: ctaDetails.image, alt: "", onLoad: () => setImageLoaded(true), style: { display: "none" } }),
|
|
193
|
+
/* @__PURE__ */ jsx(
|
|
194
|
+
motion.div,
|
|
195
|
+
{
|
|
196
|
+
style: {
|
|
197
|
+
position: "absolute",
|
|
198
|
+
inset: 0,
|
|
199
|
+
background: "linear-gradient(to top, rgba(0,0,0,0.3) 0%, transparent 50%)"
|
|
200
|
+
},
|
|
201
|
+
whileHover: {
|
|
202
|
+
background: "linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 60%)"
|
|
203
|
+
},
|
|
204
|
+
transition: { duration: 0.3 }
|
|
205
|
+
}
|
|
206
|
+
),
|
|
207
|
+
/* @__PURE__ */ jsx(
|
|
208
|
+
motion.div,
|
|
209
|
+
{
|
|
210
|
+
onClick: handleClose,
|
|
211
|
+
style: {
|
|
212
|
+
position: "absolute",
|
|
213
|
+
top: 10,
|
|
214
|
+
right: 10,
|
|
215
|
+
display: "flex",
|
|
216
|
+
alignItems: "center",
|
|
217
|
+
justifyContent: "center",
|
|
218
|
+
width: 28,
|
|
219
|
+
height: 28,
|
|
220
|
+
borderRadius: "50%",
|
|
221
|
+
background: "rgba(0,0,0,0.5)",
|
|
222
|
+
backdropFilter: "blur(8px)",
|
|
223
|
+
color: "rgba(255,255,255,0.8)",
|
|
224
|
+
cursor: "pointer"
|
|
225
|
+
},
|
|
226
|
+
whileHover: {
|
|
227
|
+
background: "rgba(0,0,0,0.7)",
|
|
228
|
+
color: "rgba(255,255,255,1)",
|
|
229
|
+
scale: 1.1
|
|
230
|
+
},
|
|
231
|
+
whileTap: { scale: 0.95 },
|
|
232
|
+
transition: { type: "spring", stiffness: 400, damping: 15 },
|
|
233
|
+
children: /* @__PURE__ */ jsx(X, { size: 14, strokeWidth: 2.5 })
|
|
234
|
+
}
|
|
235
|
+
),
|
|
236
|
+
/* @__PURE__ */ jsxs(
|
|
237
|
+
"div",
|
|
238
|
+
{
|
|
239
|
+
style: {
|
|
240
|
+
position: "absolute",
|
|
241
|
+
bottom: 12,
|
|
242
|
+
right: 12,
|
|
243
|
+
display: "flex",
|
|
244
|
+
gap: 8
|
|
245
|
+
},
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ jsx(
|
|
248
|
+
CtaButton,
|
|
249
|
+
{
|
|
250
|
+
label: "Buy Slot",
|
|
251
|
+
icon: /* @__PURE__ */ jsx(ShoppingBag, { size: 14, strokeWidth: 2.5 }),
|
|
252
|
+
onClick: handleBuySlotClick,
|
|
253
|
+
showLabel: !isSmall
|
|
254
|
+
}
|
|
255
|
+
),
|
|
256
|
+
/* @__PURE__ */ jsx(
|
|
257
|
+
CtaButton,
|
|
258
|
+
{
|
|
259
|
+
label: "Learn More",
|
|
260
|
+
icon: /* @__PURE__ */ jsx(ExternalLink, { size: 14, strokeWidth: 2.5 }),
|
|
261
|
+
onClick: (e) => {
|
|
262
|
+
e.stopPropagation();
|
|
263
|
+
handleAdClick();
|
|
264
|
+
},
|
|
265
|
+
showLabel: !isSmall
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
/* @__PURE__ */ jsxs(
|
|
272
|
+
"div",
|
|
273
|
+
{
|
|
274
|
+
style: {
|
|
275
|
+
position: "absolute",
|
|
276
|
+
top: 10,
|
|
277
|
+
left: 10,
|
|
278
|
+
display: "flex",
|
|
279
|
+
alignItems: "center",
|
|
280
|
+
gap: 4,
|
|
281
|
+
padding: "4px 8px",
|
|
282
|
+
borderRadius: 6,
|
|
283
|
+
background: "rgba(0,0,0,0.5)",
|
|
284
|
+
backdropFilter: "blur(8px)",
|
|
285
|
+
color: "rgba(255,255,255,0.8)",
|
|
286
|
+
fontSize: 10,
|
|
287
|
+
fontWeight: 500,
|
|
288
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
289
|
+
letterSpacing: "0.5px",
|
|
290
|
+
textTransform: "uppercase"
|
|
291
|
+
},
|
|
292
|
+
children: [
|
|
293
|
+
/* @__PURE__ */ jsx(Sparkles, { size: 10 }),
|
|
294
|
+
"Ad"
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
)
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export { AdsterixWidget };
|
|
304
|
+
//# sourceMappingURL=index.mjs.map
|
|
305
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/AdsterixWidget.tsx"],"names":["width"],"mappings":"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","file":"index.mjs","sourcesContent":["import * as React from \"react\"\nimport { motion, AnimatePresence } from \"framer-motion\"\nimport { ExternalLink, Sparkles, ShoppingBag, X } from \"lucide-react\"\n\nexport interface AdsterixWidgetProps {\n castHash?: string\n onClose?: () => void\n width?: string | number\n height?: string | number\n}\n\ninterface CtaDetails {\n image: string\n url: string\n buySlotUrl: string\n}\n\nconst CtaButton: React.FC<{\n label: string\n icon: React.ReactNode\n onClick: (e: React.MouseEvent) => void\n showLabel: boolean\n}> = ({ label, icon, onClick, showLabel }) => (\n <motion.div\n onClick={onClick}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: showLabel ? 6 : 0,\n padding: showLabel ? \"8px 14px\" : \"8px\",\n borderRadius: 20,\n background: \"rgba(255,255,255,0.9)\",\n backdropFilter: \"blur(8px)\",\n color: \"#0f172a\",\n fontSize: 13,\n fontWeight: 600,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\n cursor: \"pointer\",\n }}\n whileHover={{\n y: -2,\n background: \"rgba(255,255,255,0.98)\",\n boxShadow: \"0 8px 20px rgba(0,0,0,0.2)\",\n }}\n transition={{ type: \"spring\", stiffness: 400, damping: 15 }}\n >\n {showLabel && label}\n <motion.span\n style={{ display: \"flex\", alignItems: \"center\" }}\n whileHover={{ x: showLabel ? 2 : 0 }}\n transition={{ type: \"spring\", stiffness: 400, damping: 15 }}\n >\n {icon}\n </motion.span>\n </motion.div>\n)\n\nexport const AdsterixWidget: React.FC<AdsterixWidgetProps> = ({ castHash, onClose, width = \"100%\", height }) => {\n const [ctaDetails, setCtaDetails] = React.useState<CtaDetails | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(false)\n const [imageLoaded, setImageLoaded] = React.useState(false)\n const [visible, setVisible] = React.useState(true)\n const [isSmall, setIsSmall] = React.useState(true) // Start with true to avoid flash\n const containerRef = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n if (!castHash) return\n\n const fetchCtaDetails = async () => {\n setLoading(true)\n setError(null)\n\n try {\n const response = await fetch(`https://www.adsterix.xyz/api/ads/cta-details/${castHash}`)\n\n if (!response.ok) {\n throw new Error(`Failed to fetch: ${response.status}`)\n }\n\n const data = await response.json()\n setCtaDetails(data)\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Unknown error\")\n } finally {\n setLoading(false)\n }\n }\n\n fetchCtaDetails()\n }, [castHash])\n\n React.useEffect(() => {\n if (!containerRef.current) return\n\n const checkSize = (entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const width = entry.contentRect.width\n setIsSmall(width < 270)\n }\n }\n\n const resizeObserver = new ResizeObserver(checkSize)\n resizeObserver.observe(containerRef.current)\n\n // Initial check\n if (containerRef.current) {\n const width = containerRef.current.offsetWidth\n setIsSmall(width < 400)\n }\n\n return () => resizeObserver.disconnect()\n }, [loading, ctaDetails]) // Re-run when content changes\n\n const handleAdClick = () => {\n if (ctaDetails?.url) {\n window.open(ctaDetails.url, \"_blank\", \"noopener,noreferrer\")\n }\n }\n\n const handleBuySlotClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n if (ctaDetails?.buySlotUrl) window.open(ctaDetails.buySlotUrl, \"_blank\", \"noopener,noreferrer\")\n }\n\n const handleClose = (e: React.MouseEvent) => {\n e.stopPropagation()\n setVisible(false)\n onClose?.()\n }\n\n const containerStyle: React.CSSProperties = {\n position: \"relative\",\n width: typeof width === \"number\" ? `${width}px` : width,\n ...(height ? { height: typeof height === \"number\" ? `${height}px` : height } : { aspectRatio: \"3 / 2\" }),\n borderRadius: 12,\n overflow: \"hidden\",\n }\n\n if (!visible) return null\n\n if (error) {\n return (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n style={{\n ...containerStyle,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n background: \"linear-gradient(135deg, #0f172a 0%, #1e293b 100%)\",\n color: \"#f87171\",\n fontSize: 14,\n fontWeight: 500,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n }}\n >\n Unable to load ad\n </motion.div>\n )\n }\n\n if (loading || !ctaDetails) {\n return (\n <div\n ref={containerRef}\n style={{\n ...containerStyle,\n background: \"linear-gradient(135deg, #0f172a 0%, #1e293b 100%)\",\n }}\n >\n <motion.div\n style={{\n position: \"absolute\",\n inset: 0,\n background: \"linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.05) 50%, transparent 100%)\",\n }}\n animate={{ x: [\"-100%\", \"100%\"] }}\n transition={{ duration: 1.5, repeat: Infinity, ease: \"linear\" }}\n />\n </div>\n )\n }\n\n return (\n <motion.div\n ref={containerRef}\n onClick={handleAdClick}\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.95 }}\n whileHover={{ scale: 1.02 }}\n whileTap={{ scale: 0.98 }}\n transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n style={{\n ...containerStyle,\n cursor: \"pointer\",\n boxShadow: \"0 10px 30px rgba(0,0,0,0.2)\",\n }}\n >\n <AnimatePresence>\n {imageLoaded && (\n <motion.img\n src={ctaDetails.image}\n alt=\"Advertisement\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.4 }}\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n }}\n />\n )}\n </AnimatePresence>\n\n <img src={ctaDetails.image} alt=\"\" onLoad={() => setImageLoaded(true)} style={{ display: \"none\" }} />\n\n <motion.div\n style={{\n position: \"absolute\",\n inset: 0,\n background: \"linear-gradient(to top, rgba(0,0,0,0.3) 0%, transparent 50%)\",\n }}\n whileHover={{\n background: \"linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 60%)\",\n }}\n transition={{ duration: 0.3 }}\n />\n\n {/* Close button */}\n <motion.div\n onClick={handleClose}\n style={{\n position: \"absolute\",\n top: 10,\n right: 10,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 28,\n height: 28,\n borderRadius: \"50%\",\n background: \"rgba(0,0,0,0.5)\",\n backdropFilter: \"blur(8px)\",\n color: \"rgba(255,255,255,0.8)\",\n cursor: \"pointer\",\n }}\n whileHover={{\n background: \"rgba(0,0,0,0.7)\",\n color: \"rgba(255,255,255,1)\",\n scale: 1.1,\n }}\n whileTap={{ scale: 0.95 }}\n transition={{ type: \"spring\", stiffness: 400, damping: 15 }}\n >\n <X size={14} strokeWidth={2.5} />\n </motion.div>\n\n <div\n style={{\n position: \"absolute\",\n bottom: 12,\n right: 12,\n display: \"flex\",\n gap: 8,\n }}\n >\n <CtaButton\n label=\"Buy Slot\"\n icon={<ShoppingBag size={14} strokeWidth={2.5} />}\n onClick={handleBuySlotClick}\n showLabel={!isSmall}\n />\n <CtaButton\n label=\"Learn More\"\n icon={<ExternalLink size={14} strokeWidth={2.5} />}\n onClick={(e) => {\n e.stopPropagation()\n handleAdClick()\n }}\n showLabel={!isSmall}\n />\n </div>\n\n <div\n style={{\n position: \"absolute\",\n top: 10,\n left: 10,\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n padding: \"4px 8px\",\n borderRadius: 6,\n background: \"rgba(0,0,0,0.5)\",\n backdropFilter: \"blur(8px)\",\n color: \"rgba(255,255,255,0.8)\",\n fontSize: 10,\n fontWeight: 500,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n letterSpacing: \"0.5px\",\n textTransform: \"uppercase\",\n }}\n >\n <Sparkles size={10} />\n Ad\n </div>\n </motion.div>\n )\n}\n"]}
|