uikit/dist/remote-ui-app-header.js
2026-10-03 19:53:24 +03:30

145 lines
4.2 KiB
JavaScript

"use client";
import React, { useEffect, useRef } from "react";
const DEFAULT_REMOTE_URL = "https://uikit.d.aiengines.ir/AppHeader.js";
const APP_HEADER_SLOT = Symbol.for("uikit.remote.app-header.slot");
const remotePromises = new Map();
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);
}
function createRemoteElementComponent({ componentName, tagName, defaultRemoteUrl, normalizeProps }) {
return function RemoteElementComponent(props) {
const containerRef = useRef(null);
const elementRef = useRef(null);
const latestPropsRef = useRef(null);
latestPropsRef.current = normalizeProps ? normalizeProps(props) : props;
useEffect(() => {
let active = true;
loadRemote(componentName, defaultRemoteUrl)
.then(() => {
if (!active || !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) {
elementRef.current.componentProps = latestPropsRef.current;
}
});
return React.createElement("span", {
ref: containerRef,
style: { display: "contents" },
});
};
}
function createAppHeaderSlot(name) {
function AppHeaderSlot() {
return null;
}
AppHeaderSlot[APP_HEADER_SLOT] = name;
return AppHeaderSlot;
}
const BrandSection = createAppHeaderSlot("brandSection");
const Logo = createAppHeaderSlot("logo");
const Brand = createAppHeaderSlot("brand");
const Title = createAppHeaderSlot("title");
const Description = createAppHeaderSlot("description");
const Bi = createAppHeaderSlot("bi");
function toPlainText(value) {
if (value === null || value === undefined || typeof value === "boolean") return undefined;
if (typeof value === "string" || typeof value === "number") return String(value);
if (Array.isArray(value)) return value.map(toPlainText).filter(Boolean).join("");
if (React.isValidElement(value)) return toPlainText(value.props?.children);
return undefined;
}
function extractCompoundAppHeader(children) {
const result = { showBi: false };
function visit(node) {
React.Children.forEach(node, (child) => {
if (!React.isValidElement(child)) return;
const slot = child.type?.[APP_HEADER_SLOT];
if (slot === "logo") {
result.logoSrc = child.props.src;
result.logoAlt = child.props.alt;
} else if (slot === "title") {
result.title = toPlainText(child.props.children);
} else if (slot === "description") {
result.description = toPlainText(child.props.children);
} else if (slot === "bi") {
result.showBi = true;
result.biLogoSrc = child.props.logoSrc || child.props.src;
result.biLogoAlt = child.props.alt;
result.biText = child.props.text;
}
if (child.props?.children) visit(child.props.children);
});
}
visit(children);
return result;
}
const RemoteAppHeaderElement = createRemoteElementComponent({
componentName: "AppHeader",
tagName: "uikit-app-header",
defaultRemoteUrl: DEFAULT_REMOTE_URL,
normalizeProps: ({ children, ...props }) =>
children === undefined || children === null
? props
: { ...props, ...extractCompoundAppHeader(children) },
});
function AppHeaderRoot(props) {
return React.createElement(RemoteAppHeaderElement, props);
}
export const AppHeader = Object.assign(AppHeaderRoot, {
BrandSection,
Logo,
Brand,
Title,
Description,
Bi,
});