{"version":3,"file":"ArtifactPanel-BFkm8h3G.cjs","names":["Component","X","useTheme"],"sources":["../src/components/_shared/artifact/ArtifactPanel.tsx"],"sourcesContent":["import { useArtifact, useArtifactPortalTarget } from \"@openuidev/react-headless\";\nimport clsx from \"clsx\";\nimport { X } from \"lucide-react\";\nimport { Component, forwardRef, useEffect, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useTheme } from \"../../ThemeProvider/ThemeProvider\";\n\n/** @internal */\ntype ArtifactErrorBoundaryProps = {\n  children: ReactNode;\n  fallback?: ReactNode;\n};\n\ntype ArtifactErrorBoundaryState = {\n  hasError: boolean;\n};\n\n/** @internal */\nclass ArtifactErrorBoundary extends Component<\n  ArtifactErrorBoundaryProps,\n  ArtifactErrorBoundaryState\n> {\n  constructor(props: ArtifactErrorBoundaryProps) {\n    super(props);\n    this.state = { hasError: false };\n  }\n\n  static getDerivedStateFromError(): ArtifactErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  override render() {\n    if (this.state.hasError) {\n      return this.props.fallback ?? null;\n    }\n    return this.props.children;\n  }\n}\n\n/**\n * Props for {@link ArtifactPanel}.\n *\n * @category Components\n */\nexport type ArtifactPanelProps = {\n  /** Artifact ID this panel renders content for. Must match the ID passed to `useArtifact(id)`. */\n  artifactId: string;\n  /** Content rendered inside the panel when this artifact is active. */\n  children: ReactNode;\n  /** Display title for the panel header and aria-label. Defaults to `\"Artifact\"`. */\n  title?: string;\n  /** Additional CSS class name(s) applied to the panel container. */\n  className?: string;\n  /** Fallback UI rendered if children throw during rendering. Defaults to `null`. */\n  errorFallback?: ReactNode;\n  /**\n   * Controls the panel header.\n   * - `true` (default): built-in header with title + close button\n   * - `false`: no header, raw children only\n   * - `ReactNode`: custom header replacing the built-in one\n   */\n  header?: boolean | ReactNode;\n};\n\n/** @internal */\nconst DefaultHeader = ({ title, onClose }: { title: string; onClose: () => void }) => (\n  <div className=\"openui-artifact-panel__header\">\n    <span className=\"openui-artifact-panel__title\">{title}</span>\n    <button\n      className=\"openui-artifact-panel__close\"\n      onClick={onClose}\n      aria-label=\"Close artifact panel\"\n    >\n      <X size={16} />\n    </button>\n  </div>\n);\n\n/**\n * Portals artifact content into the nearest {@link ArtifactPortalTarget}.\n *\n * Renders nothing when the artifact is inactive or no portal target is mounted.\n * Wraps children in an error boundary and applies theme-scoped class names.\n *\n * Requires `<ArtifactPortalTarget />` to be mounted in the layout.\n *\n * @category Components\n */\nexport const ArtifactPanel = forwardRef<HTMLDivElement, ArtifactPanelProps>(\n  ({ artifactId, children, title, className, errorFallback, header = true }, ref) => {\n    const { isActive, close } = useArtifact(artifactId);\n    const { node: panelNode } = useArtifactPortalTarget();\n    const { portalThemeClassName } = useTheme();\n\n    useEffect(() => {\n      if (!isActive || panelNode) return;\n\n      const timer = setTimeout(() => {\n        console.warn(\n          \"[OpenUI] ArtifactPanel: artifact is active but no render target is mounted. \" +\n            \"Ensure <ArtifactPortalTarget /> is rendered in your layout.\",\n        );\n      }, 100);\n      return () => clearTimeout(timer);\n    }, [isActive, panelNode]);\n\n    if (!isActive || !panelNode) return null;\n\n    const handleClose = () => close();\n\n    let headerContent: ReactNode = null;\n    if (header === true) {\n      headerContent = <DefaultHeader title={title ?? \"Artifact\"} onClose={handleClose} />;\n    } else if (header !== false) {\n      headerContent = header;\n    }\n\n    return createPortal(\n      <div\n        ref={ref}\n        id={`openui-artifact-panel-${artifactId}`}\n        className={clsx(\"openui-artifact-panel\", portalThemeClassName, className)}\n        role=\"region\"\n        aria-label={title ?? \"Artifact panel\"}\n      >\n        {headerContent}\n        <ArtifactErrorBoundary fallback={errorFallback}>{children}</ArtifactErrorBoundary>\n      </div>,\n      panelNode,\n    );\n  },\n);\n\nArtifactPanel.displayName = \"ArtifactPanel\";\n"],"mappings":";;;;;;;;;;;AAkBA,IAAM,wBAAN,cAAoCA,MAAAA,UAGlC;CACA,YAAY,OAAmC;AAC7C,QAAM,MAAM;AACZ,OAAK,QAAQ,EAAE,UAAU,OAAO;;CAGlC,OAAO,2BAAuD;AAC5D,SAAO,EAAE,UAAU,MAAM;;CAG3B,SAAkB;AAChB,MAAI,KAAK,MAAM,SACb,QAAO,KAAK,MAAM,YAAY;AAEhC,SAAO,KAAK,MAAM;;;;AA8BtB,MAAM,iBAAiB,EAAE,OAAO,cAC9B,iBAAA,GAAA,kBAAA,MAAC,OAAD;CAAK,WAAU;WAAf,CACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAAgC;EAAa,CAAA,EAC7D,iBAAA,GAAA,kBAAA,KAAC,UAAD;EACE,WAAU;EACV,SAAS;EACT,cAAW;YAEX,iBAAA,GAAA,kBAAA,KAACC,aAAAA,GAAD,EAAG,MAAM,IAAM,CAAA;EACR,CAAA,CACL;;;;;;;;;;;;AAaR,MAAa,iBAAA,GAAA,MAAA,aACV,EAAE,YAAY,UAAU,OAAO,WAAW,eAAe,SAAS,QAAQ,QAAQ;CACjF,MAAM,EAAE,UAAU,WAAA,GAAA,0BAAA,aAAsB,WAAW;CACnD,MAAM,EAAE,MAAM,eAAA,GAAA,0BAAA,0BAAuC;CACrD,MAAM,EAAE,yBAAyBC,sBAAAA,UAAU;AAE3C,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,CAAC,YAAY,UAAW;EAE5B,MAAM,QAAQ,iBAAiB;AAC7B,WAAQ,KACN,0IAED;KACA,IAAI;AACP,eAAa,aAAa,MAAM;IAC/B,CAAC,UAAU,UAAU,CAAC;AAEzB,KAAI,CAAC,YAAY,CAAC,UAAW,QAAO;CAEpC,MAAM,oBAAoB,OAAO;CAEjC,IAAI,gBAA2B;AAC/B,KAAI,WAAW,KACb,iBAAgB,iBAAA,GAAA,kBAAA,KAAC,eAAD;EAAe,OAAO,SAAS;EAAY,SAAS;EAAe,CAAA;UAC1E,WAAW,MACpB,iBAAgB;AAGlB,SAAA,GAAA,UAAA,cACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EACO;EACL,IAAI,yBAAyB;EAC7B,YAAA,GAAA,KAAA,SAAgB,yBAAyB,sBAAsB,UAAU;EACzE,MAAK;EACL,cAAY,SAAS;YALvB,CAOG,eACD,iBAAA,GAAA,kBAAA,KAAC,uBAAD;GAAuB,UAAU;GAAgB;GAAiC,CAAA,CAC9E;KACN,UACD;EAEJ;AAED,cAAc,cAAc"}