@nektarlabs/adsterix-widget 1.1.0 → 1.4.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,12 +53,27 @@ 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 |
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 |
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
+ | `defaultImage` | `string` | — | Default image URL to display when no buyer has purchased the ad slot |
63
+ | `showAdSparkleLabel` | `boolean` | `false` | Whether to show the "Ad ✨" label overlay |
64
+ | `showCloseButton` | `boolean` | `false` | Whether to show the close button |
65
+ | `showBuySlotButton` | `boolean` | `false` | Whether to show the "Buy Slot" button |
66
+ | `showCtaButton` | `boolean` | `false` | Whether to show the CTA (call-to-action) button |
67
+ | `showCtaButtonIcon` | `boolean` | `false` | Whether to show the external link icon on the CTA button |
68
+ | `ctaButtonText` | `string` | `"Learn More"` | Custom text for the CTA button |
69
+ | `ctaNodes` | `React.ReactNode[]` | — | Custom nodes to render instead of the default CTA / Buy Slot buttons |
70
+ | `onCtaNodeClick` | `(index: number, e: React.MouseEvent, ctaDetails: CtaDetails \| null) => void` | — | Callback fired when a custom CTA node is clicked, receives the node index, event and details |
71
+ | `onBuySlotClick` | `(buySlotUrl: string) => void` | — | Callback fired when the "Buy Slot" button is clicked, receives the buy slot URL |
72
+ | `onAdClick` | `(url: string) => void` | — | Callback fired when the CTA button is clicked, receives the ad's target URL |
73
+ | `position` | `"bottom-left" \| "bottom-right" \| "top-left" \| "top-right" \| "bottom-center" \| "top-center" \| "center-left" \| "center-right"` | `"bottom-right"` | Position of the CTA buttons container |
74
+ | `containerStyle` | `React.CSSProperties` | — | Custom styles for the widget container |
75
+ | `buySlotButtonStyle` | `React.CSSProperties` | — | Custom styles for the "Buy Slot" button |
76
+ | `ctaButtonStyle` | `React.CSSProperties` | — | Custom styles for the CTA button |
62
77
 
63
78
  ## Example
64
79
 
package/dist/index.d.mts CHANGED
@@ -1,11 +1,69 @@
1
1
  import * as React from 'react';
2
2
 
3
+ interface CtaDetails {
4
+ image: string;
5
+ url: string;
6
+ buySlotUrl: string;
7
+ buyer?: {
8
+ fid: number;
9
+ username: string;
10
+ avatar: string;
11
+ displayName: string;
12
+ address: string;
13
+ };
14
+ }
15
+ interface UseCtaDetailsReturn {
16
+ ctaDetails: CtaDetails | null;
17
+ loading: boolean;
18
+ error: string | null;
19
+ refetch: () => void;
20
+ reset: () => void;
21
+ }
22
+ declare const useCtaDetails: (params: {
23
+ castHash: string;
24
+ }) => UseCtaDetailsReturn;
25
+
26
+ type CtaPosition = "bottom-left" | "bottom-right" | "top-left" | "top-right" | "bottom-center" | "top-center" | "center-left" | "center-right";
3
27
  interface AdsterixWidgetProps {
4
- castHash?: string;
28
+ /** The Farcaster cast hash to fetch ad details for (required) */
29
+ castHash: string;
30
+ /** Callback fired when the widget is closed via the close button */
5
31
  onClose?: () => void;
32
+ /** Width of the widget container (number = px, string = any CSS value). Default: "100%" */
6
33
  width?: string | number;
34
+ /** Height of the widget container (number = px, string = any CSS value). If omitted, uses 3:2 aspect ratio */
7
35
  height?: string | number;
36
+ /** Default image URL to display when no buyer has purchased the ad slot */
37
+ defaultImage?: string;
38
+ /** Show a small "Ad" label with sparkle icon in the top-left corner */
39
+ showAdSparkleLabel?: boolean;
40
+ /** Show a close button in the top-right corner */
41
+ showCloseButton?: boolean;
42
+ /** Show the "Buy Slot" button (allows users to purchase the ad slot) */
43
+ showBuySlotButton?: boolean;
44
+ /** Show the default CTA button */
45
+ showCtaButton?: boolean;
46
+ /** Show an external link icon inside the CTA button */
47
+ showCtaButtonIcon?: boolean;
48
+ /** Text label for the CTA button. Default: "Learn More" */
49
+ ctaButtonText?: string;
50
+ /** If provided, these nodes will be rendered instead of the default CTA / Buy Slot buttons */
51
+ ctaNodes?: React.ReactNode[];
52
+ /** Called when a custom CTA node is clicked: (index, event, ctaDetails) */
53
+ onCtaNodeClick?: (index: number, e: React.MouseEvent, ctaDetails: CtaDetails | null) => void;
54
+ /** Callback fired when the "Buy Slot" button is clicked, receives the buySlotUrl */
55
+ onBuySlotClick?: (buySlotUrl: string) => void;
56
+ /** Callback fired when the ad (or CTA button) is clicked, receives the destination url */
57
+ onAdClick?: (url: string) => void;
58
+ /** Position of the CTA buttons within the widget. Default: "bottom-right" */
59
+ position?: CtaPosition;
60
+ /** Custom styles to merge into the widget container */
61
+ containerStyle?: React.CSSProperties;
62
+ /** Custom styles to merge into the "Buy Slot" button */
63
+ buySlotButtonStyle?: React.CSSProperties;
64
+ /** Custom styles to merge into the CTA button */
65
+ ctaButtonStyle?: React.CSSProperties;
8
66
  }
9
67
  declare const AdsterixWidget: React.FC<AdsterixWidgetProps>;
10
68
 
11
- export { AdsterixWidget, type AdsterixWidgetProps };
69
+ export { AdsterixWidget, type AdsterixWidgetProps, type CtaDetails, type UseCtaDetailsReturn, useCtaDetails };
package/dist/index.mjs CHANGED
@@ -1,76 +1,129 @@
1
1
  import * as React from 'react';
2
- import { motion, AnimatePresence } from 'framer-motion';
2
+ import { AnimatePresence, motion } from 'framer-motion';
3
3
  import { X, ShoppingBag, ExternalLink, Sparkles } from 'lucide-react';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
6
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 }) => {
7
+ var useCtaDetails = (params) => {
8
+ const { castHash } = params;
47
9
  const [ctaDetails, setCtaDetails] = React.useState(null);
10
+ const [loading, setLoading] = React.useState(Boolean(castHash.trim()));
48
11
  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 () => {
12
+ const fetchCta = React.useCallback(
13
+ async (signal) => {
14
+ const trimmedHash = castHash == null ? void 0 : castHash.trim();
15
+ if (!trimmedHash) {
16
+ setCtaDetails(null);
17
+ setLoading(false);
18
+ setError(null);
19
+ return;
20
+ }
57
21
  setLoading(true);
58
22
  setError(null);
59
23
  try {
60
- const response = await fetch(`https://www.adsterix.xyz/api/ads/cta-details/${castHash}`);
24
+ const response = await fetch(`https://www.adsterix.xyz/api/ads/cta-details/${trimmedHash}`, { signal });
61
25
  if (!response.ok) {
62
26
  throw new Error(`Failed to fetch: ${response.status}`);
63
27
  }
64
28
  const data = await response.json();
65
29
  setCtaDetails(data);
66
30
  } catch (err) {
31
+ if ((err == null ? void 0 : err.name) === "AbortError") return;
67
32
  setError(err instanceof Error ? err.message : "Unknown error");
33
+ setCtaDetails(null);
68
34
  } finally {
69
35
  setLoading(false);
70
36
  }
71
- };
72
- fetchCtaDetails();
73
- }, [castHash]);
37
+ },
38
+ [castHash]
39
+ );
40
+ React.useEffect(() => {
41
+ const controller = new AbortController();
42
+ fetchCta(controller.signal);
43
+ return () => controller.abort();
44
+ }, [fetchCta]);
45
+ const refetch = React.useCallback(() => {
46
+ fetchCta();
47
+ }, [fetchCta]);
48
+ const reset = React.useCallback(() => {
49
+ setCtaDetails(null);
50
+ setLoading(false);
51
+ setError(null);
52
+ }, []);
53
+ return { ctaDetails, loading, error, refetch, reset };
54
+ };
55
+ var useCtaDetails_default = useCtaDetails;
56
+ var getPositionStyles = (position) => {
57
+ const baseStyles = {
58
+ position: "absolute",
59
+ display: "flex",
60
+ gap: 8
61
+ };
62
+ const positionMap = {
63
+ "bottom-right": { bottom: 12, right: 12 },
64
+ "bottom-left": { bottom: 12, left: 12 },
65
+ "top-right": { top: 12, right: 12 },
66
+ "top-left": { top: 12, left: 12 },
67
+ "bottom-center": { bottom: 12, left: "50%", transform: "translateX(-50%)" },
68
+ "top-center": { top: 12, left: "50%", transform: "translateX(-50%)" },
69
+ "center-left": { top: "50%", left: 12, transform: "translateY(-50%)" },
70
+ "center-right": { top: "50%", right: 12, transform: "translateY(-50%)" }
71
+ };
72
+ return { ...baseStyles, ...positionMap[position] };
73
+ };
74
+ var CtaButton = ({ label, icon, onClick, showLabel, style = {} }) => {
75
+ const defaultStyle = {
76
+ display: "flex",
77
+ alignItems: "center",
78
+ gap: showLabel ? 6 : 0,
79
+ padding: showLabel ? "8px 14px" : "8px",
80
+ borderRadius: 20,
81
+ background: "rgba(255,255,255,0.9)",
82
+ backdropFilter: "blur(8px)",
83
+ color: "#0f172a",
84
+ fontSize: 13,
85
+ fontWeight: 600,
86
+ fontFamily: "system-ui, -apple-system, sans-serif",
87
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
88
+ cursor: "pointer"
89
+ };
90
+ const mergedStyle = { ...defaultStyle, ...style };
91
+ return /* @__PURE__ */ jsxs("div", { onClick, style: mergedStyle, children: [
92
+ showLabel && label,
93
+ /* @__PURE__ */ jsx("span", { style: { display: "flex", alignItems: "center" }, children: icon })
94
+ ] });
95
+ };
96
+ var AdsterixWidget = ({
97
+ castHash,
98
+ onClose,
99
+ width = "100%",
100
+ position = "bottom-right",
101
+ height,
102
+ defaultImage,
103
+ showAdSparkleLabel = false,
104
+ showCloseButton = false,
105
+ showBuySlotButton = false,
106
+ showCtaButton = false,
107
+ showCtaButtonIcon = false,
108
+ ctaButtonText = "Learn More",
109
+ ctaButtonStyle,
110
+ buySlotButtonStyle,
111
+ onBuySlotClick,
112
+ onAdClick,
113
+ onCtaNodeClick,
114
+ containerStyle: _containerStyle,
115
+ ctaNodes
116
+ }) => {
117
+ const { ctaDetails, loading, error } = useCtaDetails_default({ castHash });
118
+ const [imageLoaded, setImageLoaded] = React.useState(false);
119
+ const [visible, setVisible] = React.useState(true);
120
+ const [isSmall, setIsSmall] = React.useState(true);
121
+ const containerRef = React.useRef(null);
122
+ const hasBuyer = Boolean(ctaDetails == null ? void 0 : ctaDetails.buyer);
123
+ const displayImage = hasBuyer ? ctaDetails == null ? void 0 : ctaDetails.image : defaultImage;
124
+ React.useEffect(() => {
125
+ setImageLoaded(false);
126
+ }, [displayImage]);
74
127
  React.useEffect(() => {
75
128
  if (!containerRef.current) return;
76
129
  const checkSize = (entries) => {
@@ -90,11 +143,15 @@ var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
90
143
  const handleAdClick = () => {
91
144
  if (ctaDetails == null ? void 0 : ctaDetails.url) {
92
145
  window.open(ctaDetails.url, "_blank", "noopener,noreferrer");
146
+ onAdClick == null ? void 0 : onAdClick(ctaDetails.url);
93
147
  }
94
148
  };
95
149
  const handleBuySlotClick = (e) => {
96
150
  e.stopPropagation();
97
- if (ctaDetails == null ? void 0 : ctaDetails.buySlotUrl) window.open(ctaDetails.buySlotUrl, "_blank", "noopener,noreferrer");
151
+ if (ctaDetails == null ? void 0 : ctaDetails.buySlotUrl) {
152
+ window.open(ctaDetails.buySlotUrl, "_blank", "noopener,noreferrer");
153
+ onBuySlotClick == null ? void 0 : onBuySlotClick(ctaDetails == null ? void 0 : ctaDetails.buySlotUrl);
154
+ }
98
155
  };
99
156
  const handleClose = (e) => {
100
157
  e.stopPropagation();
@@ -106,15 +163,14 @@ var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
106
163
  width: typeof width === "number" ? `${width}px` : width,
107
164
  ...height ? { height: typeof height === "number" ? `${height}px` : height } : { aspectRatio: "3 / 2" },
108
165
  borderRadius: 12,
109
- overflow: "hidden"
166
+ overflow: "hidden",
167
+ ..._containerStyle
110
168
  };
111
169
  if (!visible) return null;
112
170
  if (error) {
113
171
  return /* @__PURE__ */ jsx(
114
- motion.div,
172
+ "div",
115
173
  {
116
- initial: { opacity: 0, y: 10 },
117
- animate: { opacity: 1, y: 0 },
118
174
  style: {
119
175
  ...containerStyle,
120
176
  display: "flex",
@@ -141,42 +197,32 @@ var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
141
197
  background: "linear-gradient(135deg, #0f172a 0%, #1e293b 100%)"
142
198
  },
143
199
  children: /* @__PURE__ */ jsx(
144
- motion.div,
200
+ "div",
145
201
  {
146
202
  style: {
147
203
  position: "absolute",
148
204
  inset: 0,
149
205
  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" }
206
+ }
153
207
  }
154
208
  )
155
209
  }
156
210
  );
157
211
  }
158
212
  return /* @__PURE__ */ jsxs(
159
- motion.div,
213
+ "div",
160
214
  {
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
215
  style: {
170
216
  ...containerStyle,
171
- cursor: "pointer",
172
- boxShadow: "0 10px 30px rgba(0,0,0,0.2)"
217
+ boxShadow: "0 10px 30px rgba(0,0,0,0.2)",
218
+ cursor: "pointer"
173
219
  },
174
220
  children: [
175
- /* @__PURE__ */ jsx(AnimatePresence, { children: imageLoaded && /* @__PURE__ */ jsx(
221
+ /* @__PURE__ */ jsx(AnimatePresence, { children: imageLoaded && displayImage && /* @__PURE__ */ jsx(
176
222
  motion.img,
177
223
  {
178
- src: ctaDetails.image,
179
- alt: "Advertisement",
224
+ src: displayImage,
225
+ alt: hasBuyer ? "Advertisement" : "Default placeholder",
180
226
  initial: { opacity: 0 },
181
227
  animate: { opacity: 1 },
182
228
  transition: { duration: 0.4 },
@@ -189,22 +235,8 @@ var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
189
235
  }
190
236
  }
191
237
  ) }),
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(
238
+ displayImage && /* @__PURE__ */ jsx("img", { src: displayImage, alt: "", onLoad: () => setImageLoaded(true), style: { display: "none" } }),
239
+ showCloseButton && /* @__PURE__ */ jsx(
208
240
  motion.div,
209
241
  {
210
242
  onClick: handleClose,
@@ -233,42 +265,48 @@ var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
233
265
  children: /* @__PURE__ */ jsx(X, { size: 14, strokeWidth: 2.5 })
234
266
  }
235
267
  ),
236
- /* @__PURE__ */ jsxs(
237
- "div",
238
- {
239
- style: {
240
- position: "absolute",
241
- bottom: 12,
242
- right: 12,
243
- display: "flex",
244
- gap: 8
268
+ /* @__PURE__ */ jsx("div", { style: getPositionStyles(position), children: ctaNodes && ctaNodes.length > 0 ? (
269
+ // Render custom nodes provided by the consumer. Each node is wrapped to
270
+ // keep spacing consistent with the default layout and to avoid
271
+ // accidental propagation of clicks to the ad container.
272
+ ctaNodes.map((node, idx) => /* @__PURE__ */ jsx(
273
+ "div",
274
+ {
275
+ style: { display: "flex", alignItems: "center" },
276
+ onClick: (e) => {
277
+ e.stopPropagation();
278
+ onCtaNodeClick == null ? void 0 : onCtaNodeClick(idx, e, ctaDetails);
279
+ },
280
+ children: node
245
281
  },
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(
282
+ idx
283
+ ))
284
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
285
+ showBuySlotButton && /* @__PURE__ */ jsx(
286
+ CtaButton,
287
+ {
288
+ label: "Buy Slot",
289
+ icon: /* @__PURE__ */ jsx(ShoppingBag, { size: 14, strokeWidth: 2.5 }),
290
+ onClick: handleBuySlotClick,
291
+ showLabel: !isSmall,
292
+ style: buySlotButtonStyle
293
+ }
294
+ ),
295
+ showCtaButton && /* @__PURE__ */ jsx(
296
+ CtaButton,
297
+ {
298
+ label: ctaButtonText,
299
+ icon: showCtaButtonIcon && /* @__PURE__ */ jsx(ExternalLink, { size: 14, strokeWidth: 2.5 }),
300
+ onClick: (e) => {
301
+ e.stopPropagation();
302
+ handleAdClick();
303
+ },
304
+ showLabel: !isSmall,
305
+ style: ctaButtonStyle
306
+ }
307
+ )
308
+ ] }) }),
309
+ showAdSparkleLabel && /* @__PURE__ */ jsxs(
272
310
  "div",
273
311
  {
274
312
  style: {
@@ -300,6 +338,6 @@ var AdsterixWidget = ({ castHash, onClose, width = "100%", height }) => {
300
338
  );
301
339
  };
302
340
 
303
- export { AdsterixWidget };
341
+ export { AdsterixWidget, useCtaDetails };
304
342
  //# sourceMappingURL=index.mjs.map
305
343
  //# sourceMappingURL=index.mjs.map
@@ -1 +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"]}
1
+ {"version":3,"sources":["../src/hooks/useCtaDetails.ts","../src/AdsterixWidget.tsx"],"names":["React2","width"],"mappings":"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","file":"index.mjs","sourcesContent":["import * as React from \"react\"\n\nexport interface CtaDetails {\n image: string\n url: string\n buySlotUrl: string\n buyer?: {\n fid: number\n username: string\n avatar: string\n displayName: string\n address: string\n }\n}\n\nexport interface UseCtaDetailsReturn {\n ctaDetails: CtaDetails | null\n loading: boolean\n error: string | null\n refetch: () => void\n reset: () => void\n}\n\nexport const useCtaDetails = (params: { castHash: string }): UseCtaDetailsReturn => {\n const { castHash } = params\n const [ctaDetails, setCtaDetails] = React.useState<CtaDetails | null>(null)\n // Start loading if we have a valid castHash to avoid flash of empty state\n const [loading, setLoading] = React.useState(Boolean(castHash.trim()))\n const [error, setError] = React.useState<string | null>(null)\n\n const fetchCta = React.useCallback(\n async (signal?: AbortSignal) => {\n const trimmedHash = castHash?.trim()\n if (!trimmedHash) {\n setCtaDetails(null)\n setLoading(false)\n setError(null)\n return\n }\n\n setLoading(true)\n setError(null)\n\n try {\n const response = await fetch(`https://www.adsterix.xyz/api/ads/cta-details/${trimmedHash}`, { signal })\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 if ((err as any)?.name === \"AbortError\") return\n setError(err instanceof Error ? err.message : \"Unknown error\")\n setCtaDetails(null)\n } finally {\n setLoading(false)\n }\n },\n [castHash],\n )\n\n React.useEffect(() => {\n const controller = new AbortController()\n fetchCta(controller.signal)\n return () => controller.abort()\n }, [fetchCta])\n\n const refetch = React.useCallback(() => {\n fetchCta()\n }, [fetchCta])\n\n const reset = React.useCallback(() => {\n setCtaDetails(null)\n setLoading(false)\n setError(null)\n }, [])\n\n return { ctaDetails, loading, error, refetch, reset }\n}\n\nexport default useCtaDetails\n","import * as React from \"react\"\nimport { motion, AnimatePresence } from \"framer-motion\"\nimport { ExternalLink, Sparkles, ShoppingBag, X } from \"lucide-react\"\nimport useCtaDetails, { CtaDetails } from \"./hooks/useCtaDetails\"\n\ntype CtaPosition =\n | \"bottom-left\"\n | \"bottom-right\"\n | \"top-left\"\n | \"top-right\"\n | \"bottom-center\"\n | \"top-center\"\n | \"center-left\"\n | \"center-right\"\n\nexport interface AdsterixWidgetProps {\n /** The Farcaster cast hash to fetch ad details for (required) */\n castHash: string\n /** Callback fired when the widget is closed via the close button */\n onClose?: () => void\n /** Width of the widget container (number = px, string = any CSS value). Default: \"100%\" */\n width?: string | number\n /** Height of the widget container (number = px, string = any CSS value). If omitted, uses 3:2 aspect ratio */\n height?: string | number\n /** Default image URL to display when no buyer has purchased the ad slot */\n defaultImage?: string\n /** Show a small \"Ad\" label with sparkle icon in the top-left corner */\n showAdSparkleLabel?: boolean\n /** Show a close button in the top-right corner */\n showCloseButton?: boolean\n /** Show the \"Buy Slot\" button (allows users to purchase the ad slot) */\n showBuySlotButton?: boolean\n /** Show the default CTA button */\n showCtaButton?: boolean\n /** Show an external link icon inside the CTA button */\n showCtaButtonIcon?: boolean\n /** Text label for the CTA button. Default: \"Learn More\" */\n ctaButtonText?: string\n /** If provided, these nodes will be rendered instead of the default CTA / Buy Slot buttons */\n ctaNodes?: React.ReactNode[]\n /** Called when a custom CTA node is clicked: (index, event, ctaDetails) */\n onCtaNodeClick?: (index: number, e: React.MouseEvent, ctaDetails: CtaDetails | null) => void\n /** Callback fired when the \"Buy Slot\" button is clicked, receives the buySlotUrl */\n onBuySlotClick?: (buySlotUrl: string) => void\n /** Callback fired when the ad (or CTA button) is clicked, receives the destination url */\n onAdClick?: (url: string) => void\n /** Position of the CTA buttons within the widget. Default: \"bottom-right\" */\n position?: CtaPosition\n /** Custom styles to merge into the widget container */\n containerStyle?: React.CSSProperties\n /** Custom styles to merge into the \"Buy Slot\" button */\n buySlotButtonStyle?: React.CSSProperties\n /** Custom styles to merge into the CTA button */\n ctaButtonStyle?: React.CSSProperties\n}\n\ninterface CtaButtonProps {\n label: string\n icon: React.ReactNode\n onClick: (e: React.MouseEvent) => void\n showLabel: boolean\n style?: React.CSSProperties\n}\n\nconst getPositionStyles = (position: CtaPosition): React.CSSProperties => {\n const baseStyles: React.CSSProperties = {\n position: \"absolute\",\n display: \"flex\",\n gap: 8,\n }\n\n const positionMap: Record<CtaPosition, React.CSSProperties> = {\n \"bottom-right\": { bottom: 12, right: 12 },\n \"bottom-left\": { bottom: 12, left: 12 },\n \"top-right\": { top: 12, right: 12 },\n \"top-left\": { top: 12, left: 12 },\n \"bottom-center\": { bottom: 12, left: \"50%\", transform: \"translateX(-50%)\" },\n \"top-center\": { top: 12, left: \"50%\", transform: \"translateX(-50%)\" },\n \"center-left\": { top: \"50%\", left: 12, transform: \"translateY(-50%)\" },\n \"center-right\": { top: \"50%\", right: 12, transform: \"translateY(-50%)\" },\n }\n\n return { ...baseStyles, ...positionMap[position] }\n}\n\nconst CtaButton: React.FC<CtaButtonProps> = ({ label, icon, onClick, showLabel, style = {} }) => {\n const defaultStyle: React.CSSProperties = {\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\n const mergedStyle = { ...defaultStyle, ...style }\n\n return (\n <div onClick={onClick} style={mergedStyle}>\n {showLabel && label}\n <span style={{ display: \"flex\", alignItems: \"center\" }}>{icon}</span>\n </div>\n )\n}\n\nexport const AdsterixWidget: React.FC<AdsterixWidgetProps> = ({\n castHash,\n onClose,\n width = \"100%\",\n position = \"bottom-right\",\n height,\n defaultImage,\n showAdSparkleLabel = false,\n showCloseButton = false,\n showBuySlotButton = false,\n showCtaButton = false,\n showCtaButtonIcon = false,\n ctaButtonText = \"Learn More\",\n ctaButtonStyle,\n buySlotButtonStyle,\n onBuySlotClick,\n onAdClick,\n onCtaNodeClick,\n containerStyle: _containerStyle,\n ctaNodes,\n}) => {\n const { ctaDetails, loading, error } = useCtaDetails({ castHash })\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 // Determine if the slot has a buyer\n const hasBuyer = Boolean(ctaDetails?.buyer)\n // Use default image when no buyer, otherwise use the ad image\n const displayImage = hasBuyer ? ctaDetails?.image : defaultImage\n\n // Reset imageLoaded when the display image changes\n React.useEffect(() => {\n setImageLoaded(false)\n }, [displayImage])\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 onAdClick?.(ctaDetails.url)\n }\n }\n\n const handleBuySlotClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n if (ctaDetails?.buySlotUrl) {\n window.open(ctaDetails.buySlotUrl, \"_blank\", \"noopener,noreferrer\")\n onBuySlotClick?.(ctaDetails?.buySlotUrl)\n }\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 ..._containerStyle,\n }\n\n if (!visible) return null\n\n if (error) {\n return (\n <div\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 </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 <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 />\n </div>\n )\n }\n\n return (\n <div\n style={{\n ...containerStyle,\n boxShadow: \"0 10px 30px rgba(0,0,0,0.2)\",\n cursor: \"pointer\",\n }}\n >\n <AnimatePresence>\n {imageLoaded && displayImage && (\n <motion.img\n src={displayImage}\n alt={hasBuyer ? \"Advertisement\" : \"Default placeholder\"}\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 {displayImage && (\n <img src={displayImage} alt=\"\" onLoad={() => setImageLoaded(true)} style={{ display: \"none\" }} />\n )}\n\n {/* Close button */}\n {showCloseButton && (\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\n <div style={getPositionStyles(position)}>\n {ctaNodes && ctaNodes.length > 0 ? (\n // Render custom nodes provided by the consumer. Each node is wrapped to\n // keep spacing consistent with the default layout and to avoid\n // accidental propagation of clicks to the ad container.\n ctaNodes.map((node, idx) => (\n <div\n key={idx}\n style={{ display: \"flex\", alignItems: \"center\" }}\n onClick={(e) => {\n e.stopPropagation()\n onCtaNodeClick?.(idx, e, ctaDetails)\n }}\n >\n {node}\n </div>\n ))\n ) : (\n <>\n {showBuySlotButton && (\n <CtaButton\n label=\"Buy Slot\"\n icon={<ShoppingBag size={14} strokeWidth={2.5} />}\n onClick={handleBuySlotClick}\n showLabel={!isSmall}\n style={buySlotButtonStyle}\n />\n )}\n {showCtaButton && (\n <CtaButton\n label={ctaButtonText}\n icon={showCtaButtonIcon && <ExternalLink size={14} strokeWidth={2.5} />}\n onClick={(e) => {\n e.stopPropagation()\n handleAdClick()\n }}\n showLabel={!isSmall}\n style={ctaButtonStyle}\n />\n )}\n </>\n )}\n </div>\n\n {showAdSparkleLabel && (\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 )}\n </div>\n )\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nektarlabs/adsterix-widget",
3
- "version": "1.1.0",
3
+ "version": "1.4.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",