{"version":3,"sources":["../src/campaign-detail/types.ts","../src/campaign-detail/LegionCampaignDetail.tsx"],"names":["LegionCampaignDetail"],"mappings":"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","file":"campaign-detail.mjs","sourcesContent":["/**\n * Theming props for the Campaign Detail Widget.\n * These map to camelCase query params consumed by the iframe page.\n */\nexport interface CampaignDetailThemingProps {\n  /** Container background color */\n  detailBackgroundColor?: string;\n  /** Primary text color */\n  detailTextColor?: string;\n  /** Secondary text color */\n  detailSecondaryTextColor?: string;\n  /** Border color */\n  detailBorderColor?: string;\n  /** Container border radius, e.g. \"12px\" */\n  detailBorderRadius?: string;\n  /** Container padding */\n  detailPadding?: string;\n  /** Container box shadow */\n  detailShadow?: string;\n  /** Primary accent color (CTAs, progress, links) */\n  detailPrimaryColor?: string;\n  /** Primary hover color */\n  detailPrimaryHover?: string;\n  /** Hero overlay color */\n  detailHeroOverlay?: string;\n  /** Reward badge background */\n  detailRewardBg?: string;\n  /** Reward badge text color */\n  detailRewardColor?: string;\n  /** Task complete state color */\n  detailTaskCompleteColor?: string;\n  /** Title font size, e.g. \"1.5rem\" */\n  detailTitleSize?: string;\n  /** Title font weight */\n  detailTitleWeight?: string;\n  /** Body text font size */\n  detailBodySize?: string;\n  /** Font family */\n  detailFontFamily?: string;\n  /** Button border radius */\n  detailButtonRadius?: string;\n  /** Transition duration */\n  detailTransitionDuration?: string;\n}\n\n/**\n * Core configuration props for the Campaign Detail Widget.\n */\nexport interface CampaignDetailWidgetProps extends CampaignDetailThemingProps {\n  /** Campaign UUID (required) */\n  id: string;\n  /** Base URL of the Legion application (required) */\n  baseUrl: string;\n  /** API key / bearer token for authenticated mode */\n  apiKey?: string;\n  /** Theme preset. Default: \"light\" */\n  theme?: \"light\" | \"dark\";\n  /** Widget height CSS value. Default: \"auto\" */\n  height?: string | number;\n  /** Widget width CSS value. Default: \"100%\" */\n  width?: string;\n  /** Additional CSS class name for the container */\n  className?: string;\n  /** Additional inline styles for the container */\n  style?: React.CSSProperties;\n  /** Callback when the widget iframe has loaded */\n  onLoad?: (detail: { widget: string; id: string; theme: string }) => void;\n  /** Callback when the widget iframe fails to load */\n  onError?: (message: string) => void;\n  /**\n   * Callback fired when the embedded view requests to close\n   * (user clicks the in-widget close affordance, or completes the flow).\n   * Hosts typically use this to close the modal that wraps the widget.\n   */\n  onClose?: () => void;\n  /**\n   * Callback fired when the user successfully claims / begins the campaign.\n   * Hosts can use this to refetch their campaign listing.\n   */\n  onClaim?: (detail: { id: string }) => void;\n  /**\n   * Callback fired when the embedded view requires the user to sign in\n   * (e.g. they click \"Claim Gift\" / \"Start Earning\" without a valid token).\n   * Hosts typically use this to open their own login UI, then push a fresh\n   * `auth-token` back to the widget.\n   */\n  onRequestLogin?: (detail: { id: string | null }) => void;\n}\n\n/**\n * Mapping from camelCase theming prop names to the kebab-case query\n * parameter names used by the campaign detail widget iframe URL.\n */\nexport const CAMPAIGN_DETAIL_THEMING_PROP_TO_PARAM: Record<\n  keyof CampaignDetailThemingProps,\n  string\n> = {\n  detailBackgroundColor: \"detail-background-color\",\n  detailTextColor: \"detail-text-color\",\n  detailSecondaryTextColor: \"detail-secondary-text-color\",\n  detailBorderColor: \"detail-border-color\",\n  detailBorderRadius: \"detail-border-radius\",\n  detailPadding: \"detail-padding\",\n  detailShadow: \"detail-shadow\",\n  detailPrimaryColor: \"detail-primary-color\",\n  detailPrimaryHover: \"detail-primary-hover\",\n  detailHeroOverlay: \"detail-hero-overlay\",\n  detailRewardBg: \"detail-reward-bg\",\n  detailRewardColor: \"detail-reward-color\",\n  detailTaskCompleteColor: \"detail-task-complete-color\",\n  detailTitleSize: \"detail-title-size\",\n  detailTitleWeight: \"detail-title-weight\",\n  detailBodySize: \"detail-body-size\",\n  detailFontFamily: \"detail-font-family\",\n  detailButtonRadius: \"detail-button-radius\",\n  detailTransitionDuration: \"detail-transition-duration\",\n};\n","import React, {\n  useRef,\n  useCallback,\n  useMemo,\n  useEffect,\n  forwardRef,\n  useImperativeHandle,\n} from \"react\";\nimport {\n  type CampaignDetailWidgetProps,\n  type CampaignDetailThemingProps,\n  CAMPAIGN_DETAIL_THEMING_PROP_TO_PARAM,\n} from \"./types\";\n\n/**\n * Ref handle exposed by LegionCampaignDetail for imperative control.\n */\nexport interface LegionCampaignDetailHandle {\n  /** Reload the widget iframe */\n  refresh: () => void;\n}\n\n/**\n * Build the iframe src URL from widget props.\n */\nexport function buildCampaignDetailWidgetUrl(\n  props: CampaignDetailWidgetProps\n): string {\n  const { baseUrl, id, apiKey, theme = \"light\" } = props;\n\n  const url = new URL(\n    `/widget/campaign-detail/${encodeURIComponent(id)}`,\n    baseUrl\n  );\n  url.searchParams.set(\"theme\", theme);\n\n  if (apiKey) {\n    url.searchParams.set(\"apiKey\", apiKey);\n  }\n\n  const themingKeys = Object.keys(\n    CAMPAIGN_DETAIL_THEMING_PROP_TO_PARAM\n  ) as Array<keyof CampaignDetailThemingProps>;\n  for (const key of themingKeys) {\n    const value = props[key];\n    if (value !== undefined && value !== null && String(value).length > 0) {\n      url.searchParams.set(\n        CAMPAIGN_DETAIL_THEMING_PROP_TO_PARAM[key],\n        String(value)\n      );\n    }\n  }\n\n  return url.toString();\n}\n\n/**\n * LegionCampaignDetail – a React component that embeds the Legion\n * Campaign Detail widget via an iframe. Designed to be opened inside\n * a host-controlled modal: pair with `onClose` for dismissal.\n *\n * @example\n * ```tsx\n * import { LegionCampaignDetail } from '@legionhandtech/lht-react-widgets';\n *\n * function App() {\n *   const [open, setOpen] = useState(false);\n *   const [campaignId, setCampaignId] = useState<string | null>(null);\n *\n *   return (\n *     <Modal opened={open} onClose={() => setOpen(false)}>\n *       {campaignId && (\n *         <LegionCampaignDetail\n *           id={campaignId}\n *           apiKey={token}\n *           baseUrl=\"https://your-legion-domain.com\"\n *           onClose={() => setOpen(false)}\n *         />\n *       )}\n *     </Modal>\n *   );\n * }\n * ```\n */\nexport const LegionCampaignDetail = forwardRef<\n  LegionCampaignDetailHandle,\n  CampaignDetailWidgetProps\n>(function LegionCampaignDetail(props, ref) {\n  const {\n    id,\n    apiKey,\n    theme = \"light\",\n    height = \"auto\",\n    width = \"100%\",\n    className,\n    style,\n    onLoad,\n    onError,\n    onClose,\n    onClaim,\n    onRequestLogin,\n    baseUrl,\n    ...rest\n  } = props;\n\n  const iframeRef = useRef<HTMLIFrameElement>(null);\n\n  useImperativeHandle(\n    ref,\n    () => ({\n      refresh: () => {\n        const iframe = iframeRef.current;\n        if (iframe?.contentWindow) {\n          iframe.contentWindow.location.reload();\n        }\n      },\n    }),\n    []\n  );\n\n  const src = useMemo(\n    () => buildCampaignDetailWidgetUrl(props),\n    [\n      baseUrl,\n      id,\n      apiKey,\n      theme,\n      ...Object.keys(CAMPAIGN_DETAIL_THEMING_PROP_TO_PARAM).map(\n        (k) => rest[k as keyof typeof rest]\n      ),\n    ]\n  );\n\n  // Listen for postMessage events from the iframe.\n  useEffect(() => {\n    if (typeof window === \"undefined\") return;\n\n    let expectedOrigin: string | null = null;\n    try {\n      expectedOrigin = new URL(baseUrl).origin;\n    } catch {\n      expectedOrigin = null;\n    }\n\n    const handleMessage = (event: MessageEvent) => {\n      // Reject all messages if expectedOrigin could not be derived from\n      // baseUrl, otherwise a forged event from any page could trigger\n      // onClose / onClaim / onRequestLogin in the host.\n      if (!expectedOrigin || event.origin !== expectedOrigin) return;\n      const data = event.data;\n      if (!data || typeof data !== \"object\") return;\n      if (data.type === \"CAMPAIGN_DETAIL_CLOSE\") {\n        onClose?.();\n      } else if (data.type === \"CAMPAIGN_DETAIL_CLAIMED\" && data.id) {\n        onClaim?.({ id: String(data.id) });\n      } else if (data.type === \"CAMPAIGN_DETAIL_REQUEST_LOGIN\") {\n        onRequestLogin?.({\n          id: data.id ? String(data.id) : null,\n        });\n      }\n    };\n\n    window.addEventListener(\"message\", handleMessage);\n    return () => window.removeEventListener(\"message\", handleMessage);\n  }, [baseUrl, onClose, onClaim, onRequestLogin]);\n\n  const handleLoad = useCallback(() => {\n    const iframe = iframeRef.current;\n    if (iframe?.contentWindow && apiKey) {\n      iframe.contentWindow.postMessage(\n        { type: \"CONFIG\", apiKey, theme },\n        baseUrl\n      );\n    }\n    onLoad?.({ widget: \"campaign-detail\", id, theme });\n  }, [apiKey, baseUrl, id, theme, onLoad]);\n\n  const handleError = useCallback(() => {\n    onError?.(\"Failed to load campaign detail widget\");\n  }, [onError]);\n\n  const resolvedHeight =\n    typeof height === \"number\" ? `${height}px` : height || \"auto\";\n  const isFullHeight = resolvedHeight === \"auto\" || resolvedHeight === \"100%\";\n\n  const containerStyle: React.CSSProperties = {\n    display: \"block\",\n    width,\n    backgroundColor: \"transparent\",\n    ...(isFullHeight ?\n      { height: \"100%\", minHeight: \"480px\" }\n    : { height: resolvedHeight }),\n    ...style,\n  };\n\n  const iframeStyle: React.CSSProperties = {\n    border: \"none\",\n    borderRadius: \"8px\",\n    overflow: \"hidden\",\n    width: \"100%\",\n    ...(isFullHeight ?\n      { height: \"100%\", minHeight: \"480px\" }\n    : { height: resolvedHeight }),\n  };\n\n  return (\n    <div\n      className={className}\n      style={containerStyle}\n      data-testid=\"legion-campaign-detail\"\n    >\n      <iframe\n        ref={iframeRef}\n        src={src}\n        title=\"Campaign Detail Widget\"\n        width=\"100%\"\n        loading=\"lazy\"\n        style={iframeStyle}\n        onLoad={handleLoad}\n        onError={handleError}\n      />\n    </div>\n  );\n});\n"]}