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

152 lines
4.4 KiB
JavaScript

"use client";
import React, { useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";
const DEFAULT_REMOTE_URL = "https://uikit.d.aiengines.ir/AppSidebar.js";
const REACT_REMOTE_DEPS_KEY = "__UIKIT_REACT_REMOTE_DEPS__";
const APP_SIDEBAR_SLOT = Symbol.for("uikit.remote.app-sidebar.slot");
const remotePromises = new Map();
function ensureReactRemoteDeps() {
if (typeof window === "undefined") return;
const previous = window[REACT_REMOTE_DEPS_KEY] || {};
window[REACT_REMOTE_DEPS_KEY] = {
...previous,
React: previous.React || React,
createRoot: previous.createRoot || createRoot,
};
}
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 createReactRemoteElementComponent({
componentName,
tagName,
defaultRemoteUrl,
normalizeProps,
}) {
return function ReactRemoteElementComponent(props) {
const containerRef = useRef(null);
const elementRef = useRef(null);
const latestPropsRef = useRef(null);
latestPropsRef.current = normalizeProps ? normalizeProps(props) : props;
useEffect(() => {
let active = true;
ensureReactRemoteDeps();
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 createAppSidebarSlot(name) {
function AppSidebarSlot() {
return null;
}
AppSidebarSlot[APP_SIDEBAR_SLOT] = name;
return AppSidebarSlot;
}
const Exit = createAppSidebarSlot("exit");
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 extractExit(children) {
const result = { showExit: false };
React.Children.forEach(children, (child) => {
if (!React.isValidElement(child)) return;
if (child.type?.[APP_SIDEBAR_SLOT] !== "exit") return;
result.showExit = true;
result.exitLabel = child.props.label || toPlainText(child.props.children) || "خروج";
result.onExit = child.props.onClick;
result.exitDisabled = Boolean(child.props.disabled);
});
return result;
}
function normalizeIcon(icon) {
if (!icon || React.isValidElement(icon)) return icon;
if (typeof icon === "function") return React.createElement(icon);
if (typeof icon === "object" && icon?.$$typeof) {
try { return React.createElement(icon); } catch (_) { return icon; }
}
return icon;
}
function normalizeItems(items) {
if (!Array.isArray(items)) return [];
return items.map((item) => ({ ...item, icon: normalizeIcon(item?.icon) }));
}
const RemoteAppSidebarElement = createReactRemoteElementComponent({
componentName: "AppSidebar",
tagName: "uikit-app-sidebar",
defaultRemoteUrl: DEFAULT_REMOTE_URL,
normalizeProps: ({ children, items, ...props }) => ({
...props,
items: normalizeItems(items),
...(children !== undefined && children !== null ? extractExit(children) : {}),
}),
});
function AppSidebarRoot(props) {
return React.createElement(RemoteAppSidebarElement, props);
}
export const AppSidebar = Object.assign(AppSidebarRoot, { Exit });