118 lines
2.6 KiB
JavaScript
118 lines
2.6 KiB
JavaScript
"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 (
|
|
<span
|
|
ref={containerRef}
|
|
style={{
|
|
display: "contents",
|
|
}}
|
|
/>
|
|
);
|
|
};
|
|
}
|