@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 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 | 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 |
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
 
@@ -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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nektarlabs/adsterix-widget",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",