@nektarlabs/adsterix-widget 1.4.1 → 1.5.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
@@ -69,7 +69,8 @@ To display an ad, you need a cast hash of the corresponding Farcaster cast. Foll
69
69
  | `ctaNodes` | `React.ReactNode[]` | — | Custom nodes to render instead of the default CTA / Buy Slot buttons |
70
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
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 |
72
+ | `onCtaButtonClick` | `(ctaDetails: CtaDetails) => void` | — | Callback fired when the CTA button is clicked, receives the ctaDetails |
73
+ | `onAdClick` | `(ctaDetails: CtaDetails) => void` | — | Callback fired when the entire ad is clicked, receives the ctaDetails |
73
74
  | `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
75
  | `containerStyle` | `React.CSSProperties` | — | Custom styles for the widget container |
75
76
  | `buySlotButtonStyle` | `React.CSSProperties` | — | Custom styles for the "Buy Slot" button |
package/dist/index.d.mts CHANGED
@@ -53,8 +53,10 @@ interface AdsterixWidgetProps {
53
53
  onCtaNodeClick?: (index: number, e: React.MouseEvent, ctaDetails: CtaDetails | null) => void;
54
54
  /** Callback fired when the "Buy Slot" button is clicked, receives the buySlotUrl */
55
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;
56
+ /** Callback fired when the CTA (call-to-action) button is clicked, receives the ctaDetails */
57
+ onCtaButtonClick?: (ctaDetails: CtaDetails) => void;
58
+ /** Callback fired when the entire ad is clicked, receives the ctaDetails */
59
+ onAdClick?: (ctaDetails: CtaDetails) => void;
58
60
  /** Position of the CTA buttons within the widget. Default: "bottom-right" */
59
61
  position?: CtaPosition;
60
62
  /** Custom styles to merge into the widget container */
package/dist/index.mjs CHANGED
@@ -109,6 +109,7 @@ var AdsterixWidget = ({
109
109
  ctaButtonStyle,
110
110
  buySlotButtonStyle,
111
111
  onBuySlotClick,
112
+ onCtaButtonClick,
112
113
  onAdClick,
113
114
  onCtaNodeClick,
114
115
  containerStyle: _containerStyle,
@@ -141,9 +142,14 @@ var AdsterixWidget = ({
141
142
  return () => resizeObserver.disconnect();
142
143
  }, [loading, ctaDetails]);
143
144
  const handleAdClick = () => {
145
+ if (ctaDetails) {
146
+ onAdClick == null ? void 0 : onAdClick(ctaDetails);
147
+ }
148
+ };
149
+ const handleCtaButtonClick = () => {
144
150
  if (ctaDetails == null ? void 0 : ctaDetails.url) {
145
151
  window.open(ctaDetails.url, "_blank", "noopener,noreferrer");
146
- onAdClick == null ? void 0 : onAdClick(ctaDetails.url);
152
+ onCtaButtonClick == null ? void 0 : onCtaButtonClick(ctaDetails);
147
153
  }
148
154
  };
149
155
  const handleBuySlotClick = (e) => {
@@ -212,6 +218,7 @@ var AdsterixWidget = ({
212
218
  return /* @__PURE__ */ jsxs(
213
219
  "div",
214
220
  {
221
+ onClick: handleAdClick,
215
222
  style: {
216
223
  ...containerStyle,
217
224
  boxShadow: "0 10px 30px rgba(0,0,0,0.2)",
@@ -299,7 +306,7 @@ var AdsterixWidget = ({
299
306
  icon: showCtaButtonIcon && /* @__PURE__ */ jsx(ExternalLink, { size: 14, strokeWidth: 2.5 }),
300
307
  onClick: (e) => {
301
308
  e.stopPropagation();
302
- handleAdClick();
309
+ handleCtaButtonClick();
303
310
  },
304
311
  showLabel: !isSmall,
305
312
  style: ctaButtonStyle
@@ -1 +1 @@
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 // if has buyer, use ad image; else use defaultImage if provided, else use ad image\n const displayImage = hasBuyer ? ctaDetails?.image : defaultImage ? defaultImage : ctaDetails?.image\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"]}
1
+ {"version":3,"sources":["../src/hooks/useCtaDetails.ts","../src/AdsterixWidget.tsx"],"names":["React2","width"],"mappings":";;;;;;AAuBO,IAAM,aAAA,GAAgB,CAAC,MAAA,KAAsD;AAClF,EAAA,MAAM,EAAE,UAAS,GAAI,MAAA;AACrB,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAU,eAA4B,IAAI,CAAA;AAE1E,EAAA,MAAM,CAAC,SAAS,UAAU,CAAA,GAAU,eAAS,OAAA,CAAQ,QAAA,CAAS,IAAA,EAAM,CAAC,CAAA;AACrE,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM,QAAA,GAAiB,KAAA,CAAA,WAAA;AAAA,IACrB,OAAO,MAAA,KAAyB;AAC9B,MAAA,MAAM,cAAc,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,IAAA,EAAA;AAC9B,MAAA,IAAI,CAAC,WAAA,EAAa;AAChB,QAAA,aAAA,CAAc,IAAI,CAAA;AAClB,QAAA,UAAA,CAAW,KAAK,CAAA;AAChB,QAAA,QAAA,CAAS,IAAI,CAAA;AACb,QAAA;AAAA,MACF;AAEA,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,CAAA,6CAAA,EAAgD,WAAW,CAAA,CAAA,EAAI,EAAE,QAAQ,CAAA;AAEtG,QAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,UAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,QACvD;AAEA,QAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,QAAA,aAAA,CAAc,IAAI,CAAA;AAAA,MACpB,SAAS,GAAA,EAAK;AACZ,QAAA,IAAA,CAAK,GAAA,IAAA,IAAA,GAAA,MAAA,GAAA,GAAA,CAAa,UAAS,YAAA,EAAc;AACzC,QAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,eAAe,CAAA;AAC7D,QAAA,aAAA,CAAc,IAAI,CAAA;AAAA,MACpB,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,QAAA,CAAS,WAAW,MAAM,CAAA;AAC1B,IAAA,OAAO,MAAM,WAAW,KAAA,EAAM;AAAA,EAChC,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,OAAA,GAAgB,kBAAY,MAAM;AACtC,IAAA,QAAA,EAAS;AAAA,EACX,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,KAAA,GAAc,kBAAY,MAAM;AACpC,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,UAAA,CAAW,KAAK,CAAA;AAChB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,UAAA,EAAY,OAAA,EAAS,KAAA,EAAO,SAAS,KAAA,EAAM;AACtD;AAEA,IAAO,qBAAA,GAAQ,aAAA;AChBf,IAAM,iBAAA,GAAoB,CAAC,QAAA,KAA+C;AACxE,EAAA,MAAM,UAAA,GAAkC;AAAA,IACtC,QAAA,EAAU,UAAA;AAAA,IACV,OAAA,EAAS,MAAA;AAAA,IACT,GAAA,EAAK;AAAA,GACP;AAEA,EAAA,MAAM,WAAA,GAAwD;AAAA,IAC5D,cAAA,EAAgB,EAAE,MAAA,EAAQ,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,IACxC,aAAA,EAAe,EAAE,MAAA,EAAQ,EAAA,EAAI,MAAM,EAAA,EAAG;AAAA,IACtC,WAAA,EAAa,EAAE,GAAA,EAAK,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,IAClC,UAAA,EAAY,EAAE,GAAA,EAAK,EAAA,EAAI,MAAM,EAAA,EAAG;AAAA,IAChC,iBAAiB,EAAE,MAAA,EAAQ,IAAI,IAAA,EAAM,KAAA,EAAO,WAAW,kBAAA,EAAmB;AAAA,IAC1E,cAAc,EAAE,GAAA,EAAK,IAAI,IAAA,EAAM,KAAA,EAAO,WAAW,kBAAA,EAAmB;AAAA,IACpE,eAAe,EAAE,GAAA,EAAK,OAAO,IAAA,EAAM,EAAA,EAAI,WAAW,kBAAA,EAAmB;AAAA,IACrE,gBAAgB,EAAE,GAAA,EAAK,OAAO,KAAA,EAAO,EAAA,EAAI,WAAW,kBAAA;AAAmB,GACzE;AAEA,EAAA,OAAO,EAAE,GAAG,UAAA,EAAY,GAAG,WAAA,CAAY,QAAQ,CAAA,EAAE;AACnD,CAAA;AAEA,IAAM,SAAA,GAAsC,CAAC,EAAE,KAAA,EAAO,IAAA,EAAM,SAAS,SAAA,EAAW,KAAA,GAAQ,EAAC,EAAE,KAAM;AAC/F,EAAA,MAAM,YAAA,GAAoC;AAAA,IACxC,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,GAAA,EAAK,YAAY,CAAA,GAAI,CAAA;AAAA,IACrB,OAAA,EAAS,YAAY,UAAA,GAAa,KAAA;AAAA,IAClC,YAAA,EAAc,EAAA;AAAA,IACd,UAAA,EAAY,uBAAA;AAAA,IACZ,cAAA,EAAgB,WAAA;AAAA,IAChB,KAAA,EAAO,SAAA;AAAA,IACP,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY,GAAA;AAAA,IACZ,UAAA,EAAY,sCAAA;AAAA,IACZ,SAAA,EAAW,6BAAA;AAAA,IACX,MAAA,EAAQ;AAAA,GACV;AAEA,EAAA,MAAM,WAAA,GAAc,EAAE,GAAG,YAAA,EAAc,GAAG,KAAA,EAAM;AAEhD,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,OAAA,EAAkB,KAAA,EAAO,WAAA,EAC3B,QAAA,EAAA;AAAA,IAAA,SAAA,IAAa,KAAA;AAAA,oBACd,GAAA,CAAC,UAAK,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAS,EAAI,QAAA,EAAA,IAAA,EAAK;AAAA,GAAA,EAChE,CAAA;AAEJ,CAAA;AAEO,IAAM,iBAAgD,CAAC;AAAA,EAC5D,QAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA,GAAQ,MAAA;AAAA,EACR,QAAA,GAAW,cAAA;AAAA,EACX,MAAA;AAAA,EACA,YAAA;AAAA,EACA,kBAAA,GAAqB,KAAA;AAAA,EACrB,eAAA,GAAkB,KAAA;AAAA,EAClB,iBAAA,GAAoB,KAAA;AAAA,EACpB,aAAA,GAAgB,KAAA;AAAA,EAChB,iBAAA,GAAoB,KAAA;AAAA,EACpB,aAAA,GAAgB,YAAA;AAAA,EAChB,cAAA;AAAA,EACA,kBAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA,EAAgB,eAAA;AAAA,EAChB;AACF,CAAA,KAAM;AACJ,EAAA,MAAM,EAAE,YAAY,OAAA,EAAS,KAAA,KAAU,qBAAA,CAAc,EAAE,UAAU,CAAA;AACjE,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAUA,eAAS,KAAK,CAAA;AAC1D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAUA,eAAS,IAAI,CAAA;AACjD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAUA,eAAS,IAAI,CAAA;AACjD,EAAA,MAAM,YAAA,GAAqBA,aAAuB,IAAI,CAAA;AAGtD,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,UAAA,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAY,KAAK,CAAA;AAE1C,EAAA,MAAM,eAAe,QAAA,GAAW,UAAA,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAY,KAAA,GAAQ,YAAA,GAAe,eAAe,UAAA,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAY,KAAA;AAG9F,EAAMA,gBAAU,MAAM;AACpB,IAAA,cAAA,CAAe,KAAK,CAAA;AAAA,EACtB,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAEjB,EAAMA,gBAAU,MAAM;AACpB,IAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAE3B,IAAA,MAAM,SAAA,GAAY,CAAC,OAAA,KAAmC;AACpD,MAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,QAAA,MAAMC,MAAAA,GAAQ,MAAM,WAAA,CAAY,KAAA;AAChC,QAAA,UAAA,CAAWA,SAAQ,GAAG,CAAA;AAAA,MACxB;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,cAAA,GAAiB,IAAI,cAAA,CAAe,SAAS,CAAA;AACnD,IAAA,cAAA,CAAe,OAAA,CAAQ,aAAa,OAAO,CAAA;AAG3C,IAAA,IAAI,aAAa,OAAA,EAAS;AACxB,MAAA,MAAMA,MAAAA,GAAQ,aAAa,OAAA,CAAQ,WAAA;AACnC,MAAA,UAAA,CAAWA,SAAQ,GAAG,CAAA;AAAA,IACxB;AAEA,IAAA,OAAO,MAAM,eAAe,UAAA,EAAW;AAAA,EACzC,CAAA,EAAG,CAAC,OAAA,EAAS,UAAU,CAAC,CAAA;AAExB,EAAA,MAAM,gBAAgB,MAAM;AAC1B,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,SAAA,IAAA,IAAA,GAAA,MAAA,GAAA,SAAA,CAAY,UAAA,CAAA;AAAA,IACd;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,uBAAuB,MAAM;AACjC,IAAA,IAAI,yCAAY,GAAA,EAAK;AACnB,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,CAAW,GAAA,EAAK,QAAA,EAAU,qBAAqB,CAAA;AAC3D,MAAA,gBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAmB,UAAA,CAAA;AAAA,IACrB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,kBAAA,GAAqB,CAAC,CAAA,KAAwB;AAClD,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,IAAI,yCAAY,UAAA,EAAY;AAC1B,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,CAAW,UAAA,EAAY,QAAA,EAAU,qBAAqB,CAAA;AAClE,MAAA,cAAA,IAAA,IAAA,GAAA,MAAA,GAAA,cAAA,CAAiB,UAAA,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAY,UAAA,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAAwB;AAC3C,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,UAAA,CAAW,KAAK,CAAA;AAChB,IAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,EAAA;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,cAAA,GAAsC;AAAA,IAC1C,QAAA,EAAU,UAAA;AAAA,IACV,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,CAAA,EAAG,KAAK,CAAA,EAAA,CAAA,GAAO,KAAA;AAAA,IAClD,GAAI,MAAA,GAAS,EAAE,MAAA,EAAQ,OAAO,MAAA,KAAW,QAAA,GAAW,CAAA,EAAG,MAAM,CAAA,EAAA,CAAA,GAAO,MAAA,EAAO,GAAI,EAAE,aAAa,OAAA,EAAQ;AAAA,IACtG,YAAA,EAAc,EAAA;AAAA,IACd,QAAA,EAAU,QAAA;AAAA,IACV,GAAG;AAAA,GACL;AAEA,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO;AAAA,UACL,GAAG,cAAA;AAAA,UACH,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,cAAA,EAAgB,QAAA;AAAA,UAChB,OAAA,EAAS,EAAA;AAAA,UACT,UAAA,EAAY,mDAAA;AAAA,UACZ,KAAA,EAAO,SAAA;AAAA,UACP,QAAA,EAAU,EAAA;AAAA,UACV,UAAA,EAAY,GAAA;AAAA,UACZ,UAAA,EAAY;AAAA,SACd;AAAA,QACD,QAAA,EAAA;AAAA;AAAA,KAED;AAAA,EAEJ;AAEA,EAAA,IAAI,OAAA,IAAW,CAAC,UAAA,EAAY;AAC1B,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,YAAA;AAAA,QACL,KAAA,EAAO;AAAA,UACL,GAAG,cAAA;AAAA,UACH,UAAA,EAAY;AAAA,SACd;AAAA,QAEA,QAAA,kBAAA,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO;AAAA,cACL,QAAA,EAAU,UAAA;AAAA,cACV,KAAA,EAAO,CAAA;AAAA,cACP,UAAA,EAAY;AAAA;AACd;AAAA;AACF;AAAA,KACF;AAAA,EAEJ;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAS,aAAA;AAAA,MACT,KAAA,EAAO;AAAA,QACL,GAAG,cAAA;AAAA,QACH,SAAA,EAAW,6BAAA;AAAA,QACX,MAAA,EAAQ;AAAA,OACV;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,eAAA,EAAA,EACE,yBAAe,YAAA,oBACd,GAAA;AAAA,UAAC,MAAA,CAAO,GAAA;AAAA,UAAP;AAAA,YACC,GAAA,EAAK,YAAA;AAAA,YACL,GAAA,EAAK,WAAW,eAAA,GAAkB,qBAAA;AAAA,YAClC,OAAA,EAAS,EAAE,OAAA,EAAS,CAAA,EAAE;AAAA,YACtB,OAAA,EAAS,EAAE,OAAA,EAAS,CAAA,EAAE;AAAA,YACtB,UAAA,EAAY,EAAE,QAAA,EAAU,GAAA,EAAI;AAAA,YAC5B,KAAA,EAAO;AAAA,cACL,QAAA,EAAU,UAAA;AAAA,cACV,KAAA,EAAO,CAAA;AAAA,cACP,KAAA,EAAO,MAAA;AAAA,cACP,MAAA,EAAQ,MAAA;AAAA,cACR,SAAA,EAAW;AAAA;AACb;AAAA,SACF,EAEJ,CAAA;AAAA,QAEC,gCACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,YAAA,EAAc,KAAI,EAAA,EAAG,MAAA,EAAQ,MAAM,cAAA,CAAe,IAAI,CAAA,EAAG,KAAA,EAAO,EAAE,OAAA,EAAS,QAAO,EAAG,CAAA;AAAA,QAIhG,eAAA,oBACC,GAAA;AAAA,UAAC,MAAA,CAAO,GAAA;AAAA,UAAP;AAAA,YACC,OAAA,EAAS,WAAA;AAAA,YACT,KAAA,EAAO;AAAA,cACL,QAAA,EAAU,UAAA;AAAA,cACV,GAAA,EAAK,EAAA;AAAA,cACL,KAAA,EAAO,EAAA;AAAA,cACP,OAAA,EAAS,MAAA;AAAA,cACT,UAAA,EAAY,QAAA;AAAA,cACZ,cAAA,EAAgB,QAAA;AAAA,cAChB,KAAA,EAAO,EAAA;AAAA,cACP,MAAA,EAAQ,EAAA;AAAA,cACR,YAAA,EAAc,KAAA;AAAA,cACd,UAAA,EAAY,iBAAA;AAAA,cACZ,cAAA,EAAgB,WAAA;AAAA,cAChB,KAAA,EAAO,uBAAA;AAAA,cACP,MAAA,EAAQ;AAAA,aACV;AAAA,YACA,UAAA,EAAY;AAAA,cACV,UAAA,EAAY,iBAAA;AAAA,cACZ,KAAA,EAAO,qBAAA;AAAA,cACP,KAAA,EAAO;AAAA,aACT;AAAA,YACA,QAAA,EAAU,EAAE,KAAA,EAAO,IAAA,EAAK;AAAA,YACxB,YAAY,EAAE,IAAA,EAAM,UAAU,SAAA,EAAW,GAAA,EAAK,SAAS,EAAA,EAAG;AAAA,YAE1D,QAAA,kBAAA,GAAA,CAAC,CAAA,EAAA,EAAE,IAAA,EAAM,EAAA,EAAI,aAAa,GAAA,EAAK;AAAA;AAAA,SACjC;AAAA,wBAGF,GAAA,CAAC,SAAI,KAAA,EAAO,iBAAA,CAAkB,QAAQ,CAAA,EACnC,QAAA,EAAA,QAAA,IAAY,SAAS,MAAA,GAAS,CAAA;AAAA;AAAA;AAAA;AAAA,UAI7B,QAAA,CAAS,GAAA,CAAI,CAAC,IAAA,EAAM,GAAA,qBAClB,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cAEC,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,YAAY,QAAA,EAAS;AAAA,cAC/C,OAAA,EAAS,CAAC,CAAA,KAAM;AACd,gBAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,gBAAA,cAAA,IAAA,IAAA,GAAA,MAAA,GAAA,cAAA,CAAiB,KAAK,CAAA,EAAG,UAAA,CAAA;AAAA,cAC3B,CAAA;AAAA,cAEC,QAAA,EAAA;AAAA,aAAA;AAAA,YAPI;AAAA,WASR;AAAA,4BAED,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,UAAA,iBAAA,oBACC,GAAA;AAAA,YAAC,SAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAM,UAAA;AAAA,cACN,sBAAM,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAM,EAAA,EAAI,aAAa,GAAA,EAAK,CAAA;AAAA,cAC/C,OAAA,EAAS,kBAAA;AAAA,cACT,WAAW,CAAC,OAAA;AAAA,cACZ,KAAA,EAAO;AAAA;AAAA,WACT;AAAA,UAED,aAAA,oBACC,GAAA;AAAA,YAAC,SAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAO,aAAA;AAAA,cACP,MAAM,iBAAA,oBAAqB,GAAA,CAAC,gBAAa,IAAA,EAAM,EAAA,EAAI,aAAa,GAAA,EAAK,CAAA;AAAA,cACrE,OAAA,EAAS,CAAC,CAAA,KAAM;AACd,gBAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,gBAAA,oBAAA,EAAqB;AAAA,cACvB,CAAA;AAAA,cACA,WAAW,CAAC,OAAA;AAAA,cACZ,KAAA,EAAO;AAAA;AAAA;AACT,SAAA,EAEJ,CAAA,EAEJ,CAAA;AAAA,QAEC,kBAAA,oBACC,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO;AAAA,cACL,QAAA,EAAU,UAAA;AAAA,cACV,GAAA,EAAK,EAAA;AAAA,cACL,IAAA,EAAM,EAAA;AAAA,cACN,OAAA,EAAS,MAAA;AAAA,cACT,UAAA,EAAY,QAAA;AAAA,cACZ,GAAA,EAAK,CAAA;AAAA,cACL,OAAA,EAAS,SAAA;AAAA,cACT,YAAA,EAAc,CAAA;AAAA,cACd,UAAA,EAAY,iBAAA;AAAA,cACZ,cAAA,EAAgB,WAAA;AAAA,cAChB,KAAA,EAAO,uBAAA;AAAA,cACP,QAAA,EAAU,EAAA;AAAA,cACV,UAAA,EAAY,GAAA;AAAA,cACZ,UAAA,EAAY,sCAAA;AAAA,cACZ,aAAA,EAAe,OAAA;AAAA,cACf,aAAA,EAAe;AAAA,aACjB;AAAA,YAEA,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,QAAA,EAAA,EAAS,MAAM,EAAA,EAAI,CAAA;AAAA,cAAE;AAAA;AAAA;AAAA;AAExB;AAAA;AAAA,GAEJ;AAEJ","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 CTA (call-to-action) button is clicked, receives the ctaDetails */\n onCtaButtonClick?: (ctaDetails: CtaDetails) => void\n /** Callback fired when the entire ad is clicked, receives the ctaDetails */\n onAdClick?: (ctaDetails: CtaDetails) => 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 onCtaButtonClick,\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 // if has buyer, use ad image; else use defaultImage if provided, else use ad image\n const displayImage = hasBuyer ? ctaDetails?.image : defaultImage ? defaultImage : ctaDetails?.image\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) {\n onAdClick?.(ctaDetails)\n }\n }\n\n const handleCtaButtonClick = () => {\n if (ctaDetails?.url) {\n window.open(ctaDetails.url, \"_blank\", \"noopener,noreferrer\")\n onCtaButtonClick?.(ctaDetails)\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 onClick={handleAdClick}\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 handleCtaButtonClick()\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.4.1",
3
+ "version": "1.5.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,6 +23,9 @@
23
23
  "clean": "rm -rf dist",
24
24
  "dev": "tsup --watch",
25
25
  "format": "prettier --config ./.prettierrc --write \"./**/*.{ts,tsx,json,md}\"",
26
+ "test": "vitest",
27
+ "test:run": "vitest run",
28
+ "test:coverage": "vitest run --coverage",
26
29
  "semantic-release": "semantic-release"
27
30
  },
28
31
  "peerDependencies": {
@@ -32,11 +35,16 @@
32
35
  "devDependencies": {
33
36
  "@semantic-release/changelog": "^6.0.3",
34
37
  "@semantic-release/git": "^10.0.1",
38
+ "@testing-library/jest-dom": "^6.4.0",
39
+ "@testing-library/react": "^16.0.0",
35
40
  "@types/react": "^19.2.0",
36
41
  "@types/react-dom": "^19.2.0",
42
+ "@vitejs/plugin-react": "^4.2.0",
43
+ "jsdom": "^24.0.0",
37
44
  "semantic-release": "^25.0.2",
38
45
  "tsup": "^8.0.0",
39
- "typescript": "^5.0.0"
46
+ "typescript": "^5.0.0",
47
+ "vitest": "^2.0.0"
40
48
  },
41
49
  "dependencies": {
42
50
  "framer-motion": "^12.23.24",