{"version":3,"sources":["../../src/lib/create-with-fallback.tsx"],"sourcesContent":["\"use client\";\n\nimport type { WhopElement } from \"@whop/embedded-components-vanilla-js/types\";\nimport React, { useEffect, useLayoutEffect, useMemo, useRef } from \"react\";\nimport { useElementSnapshot } from \"./use-element-snapshot\";\n\n/**\n * Base element type that all created elements conform to.\n * Using WhopElement<any, any, any> gives us access to common methods\n * (mount, unmount, on, off, updateOptions) without union type issues.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype BaseElement = WhopElement<any, any, any>;\n\n/**\n * The minimal session surface the wrapper needs — the registry-typed\n * createElement every session exposes.\n */\ninterface SessionLike<Registry extends Record<string, [unknown, unknown]>> {\n\tcreateElement<T extends keyof Registry>(\n\t\ttype: T | { type: T },\n\t\toptions: Registry[T][0],\n\t): unknown;\n}\n\ninterface WithFallbackPropsBase {\n\tclassName?: string;\n\tstyle?: React.CSSProperties;\n\tonReady?: () => void;\n\tfallback?: React.ReactNode;\n}\n\nexport interface WithFallbackPropsRequired<\n\tTOptions,\n> extends WithFallbackPropsBase {\n\toptions: TOptions;\n}\n\nexport interface WithFallbackPropsOptional<\n\tTOptions,\n> extends WithFallbackPropsBase {\n\toptions?: TOptions;\n}\n\ntype WithFallbackComponent<TOptions, TRequired extends boolean> = {\n\t(\n\t\tprops: TRequired extends true\n\t\t\t? WithFallbackPropsRequired<TOptions>\n\t\t\t: WithFallbackPropsOptional<TOptions>,\n\t): React.ReactNode;\n\tdisplayName: string;\n\ttype: string;\n};\n\n/**\n * Builds a namespace-specific `withFallback` from that namespace's session\n * hook. The returned helper creates a React component that wraps an embedded\n * element with fallback support.\n *\n * @example\n * ```tsx\n * // src/wallet/with-fallback.tsx\n * export const withFallback = createWithFallback<WalletSessionElements>(useWalletSession);\n *\n * // src/wallet/elements/deposit.tsx — options type inferred from the element type\n * export const DepositElement = withFallback({\n *   displayName: \"DepositElement\",\n *   type: \"deposit-element\",\n * } as const);\n * ```\n */\nexport function createWithFallback<\n\tRegistry extends Record<string, [unknown, unknown]>,\n>(useSession: () => SessionLike<Registry> | null) {\n\treturn function withFallback<\n\t\tconst T extends keyof Registry & string,\n\t\tTRequired extends boolean = false,\n\t>(Component: {\n\t\tdisplayName: string;\n\t\ttype: T;\n\t}): WithFallbackComponent<Registry[T][0], TRequired> {\n\t\ttype TOptions = Registry[T][0];\n\n\t\tfunction WrappedElement({\n\t\t\toptions,\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\tonReady,\n\t\t\tfallback,\n\t\t}: WithFallbackPropsOptional<TOptions>) {\n\t\t\tconst session = useSession();\n\t\t\tconst ref = useRef<HTMLDivElement>(null);\n\n\t\t\tconst element = useMemo((): BaseElement | null => {\n\t\t\t\tif (!session) return null;\n\t\t\t\t// Safe: T is constrained to valid element types, and options matches T\n\t\t\t\treturn session.createElement(\n\t\t\t\t\tComponent,\n\t\t\t\t\toptions ?? ({} as TOptions),\n\t\t\t\t) as BaseElement;\n\t\t\t\t// eslint-disable-next-line react-hooks/exhaustive-deps -- element is created once per session; options updates flow through updateOptions below\n\t\t\t}, [session]);\n\n\t\t\tconst elementSnapshot = useElementSnapshot(element);\n\n\t\t\tconst isReady = elementSnapshot?.state === \"ready\";\n\n\t\t\tuseEffect(() => {\n\t\t\t\tif (!element) return;\n\t\t\t\telement.updateOptions(options ?? {});\n\t\t\t}, [options, element]);\n\n\t\t\tuseEffect(() => {\n\t\t\t\tif (!element || !onReady) return;\n\t\t\t\telement.on(\"ready\", onReady);\n\t\t\t\treturn () => {\n\t\t\t\t\telement.off(\"ready\", onReady);\n\t\t\t\t};\n\t\t\t}, [element, onReady]);\n\n\t\t\tuseLayoutEffect(() => {\n\t\t\t\tif (!element || !ref.current) return;\n\t\t\t\telement.mount(ref.current);\n\t\t\t\treturn () => {\n\t\t\t\t\telement.unmount();\n\t\t\t\t};\n\t\t\t\t// eslint-disable-next-line react-hooks/exhaustive-deps -- ref.current intentionally re-runs the mount when the container attaches\n\t\t\t}, [element, ref.current]);\n\n\t\t\treturn (\n\t\t\t\t<>\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{ ...style, visibility: isReady ? undefined : \"hidden\" }}\n\t\t\t\t\t\tclassName={className}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t/>\n\t\t\t\t\t{!isReady && (fallback ?? null)}\n\t\t\t\t</>\n\t\t\t);\n\t\t}\n\n\t\tWrappedElement.displayName = Component.displayName;\n\t\tWrappedElement.type = Component.type;\n\n\t\treturn WrappedElement as WithFallbackComponent<TOptions, TRequired>;\n\t};\n}\n"],"mappings":";;AAGA,OAAO,SAAS,WAAW,iBAAiB,SAAS,cAAc;AACnE,SAAS,0BAA0B;AAmE5B,SAAS,mBAEd,YAAgD;AACjD,SAAO,SAAS,aAGd,WAGmD;AAGpD,aAAS,eAAe;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,GAAwC;AACvC,YAAM,UAAU,WAAW;AAC3B,YAAM,MAAM,OAAuB,IAAI;AAEvC,YAAM,UAAU,QAAQ,MAA0B;AACjD,YAAI,CAAC,QAAS,QAAO;AAErB,eAAO,QAAQ;AAAA,UACd;AAAA,UACA,WAAY,CAAC;AAAA,QACd;AAAA,MAED,GAAG,CAAC,OAAO,CAAC;AAEZ,YAAM,kBAAkB,mBAAmB,OAAO;AAElD,YAAM,UAAU,iBAAiB,UAAU;AAE3C,gBAAU,MAAM;AACf,YAAI,CAAC,QAAS;AACd,gBAAQ,cAAc,WAAW,CAAC,CAAC;AAAA,MACpC,GAAG,CAAC,SAAS,OAAO,CAAC;AAErB,gBAAU,MAAM;AACf,YAAI,CAAC,WAAW,CAAC,QAAS;AAC1B,gBAAQ,GAAG,SAAS,OAAO;AAC3B,eAAO,MAAM;AACZ,kBAAQ,IAAI,SAAS,OAAO;AAAA,QAC7B;AAAA,MACD,GAAG,CAAC,SAAS,OAAO,CAAC;AAErB,sBAAgB,MAAM;AACrB,YAAI,CAAC,WAAW,CAAC,IAAI,QAAS;AAC9B,gBAAQ,MAAM,IAAI,OAAO;AACzB,eAAO,MAAM;AACZ,kBAAQ,QAAQ;AAAA,QACjB;AAAA,MAED,GAAG,CAAC,SAAS,IAAI,OAAO,CAAC;AAEzB,aACC,0DACC;AAAA,QAAC;AAAA;AAAA,UACA,OAAO,EAAE,GAAG,OAAO,YAAY,UAAU,SAAY,SAAS;AAAA,UAC9D;AAAA,UACA;AAAA;AAAA,MACD,GACC,CAAC,YAAY,YAAY,KAC3B;AAAA,IAEF;AAEA,mBAAe,cAAc,UAAU;AACvC,mBAAe,OAAO,UAAU;AAEhC,WAAO;AAAA,EACR;AACD;","names":[]}