"use client"; import React, { useEffect, useRef } from "react"; import { REMOTE_DEPS_KEY } from "./constants"; import { setupUikitRemoteDeps } from "./setupRemoteDeps"; const remotePromises = new Map(); function hasRequiredDeps(requiredDeps = []) { if (typeof window === "undefined") return false; const deps = window[REMOTE_DEPS_KEY]; return requiredDeps.every((key) => deps?.[key]); } function ensureRemoteDeps(requiredDeps = []) { if (typeof window === "undefined") return; if (!hasRequiredDeps(requiredDeps)) { setupUikitRemoteDeps(); } } function loadRemote(componentName, remoteFileUrl) { const cacheKey = `${componentName}:${remoteFileUrl}`; if (!remotePromises.has(cacheKey)) { remotePromises.set( cacheKey, import( /* webpackIgnore: true */ /* @vite-ignore */ remoteFileUrl ).catch((error) => { remotePromises.delete(cacheKey); throw error; }), ); } return remotePromises.get(cacheKey); } export function createRemoteElementComponent({ componentName, tagName, defaultRemoteUrl, requiredDeps, normalizeProps, }) { return function RemoteElementComponent({ theme, chakraProviderProps, ...props }) { const containerRef = useRef(null); const elementRef = useRef(null); const latestPropsRef = useRef(null); latestPropsRef.current = { ...(normalizeProps ? normalizeProps(props) : props), ...(theme !== undefined ? { theme } : {}), ...(chakraProviderProps ? { chakraProviderProps } : {}), }; useEffect(() => { let active = true; ensureRemoteDeps(requiredDeps); loadRemote(componentName, defaultRemoteUrl) .then(() => { if (!active) return; if (!containerRef.current) return; const element = document.createElement(tagName); element.componentProps = latestPropsRef.current; containerRef.current.appendChild(element); elementRef.current = element; }) .catch((error) => { console.error( `[uikit/remote/ui] Failed to load ${componentName}:`, error, ); }); return () => { active = false; if (elementRef.current) { elementRef.current.remove(); elementRef.current = null; } }; }, [componentName, defaultRemoteUrl, tagName]); useEffect(() => { if (!elementRef.current) return; elementRef.current.componentProps = latestPropsRef.current; }); return ( ); }; }