import { customElement, noShadowDOM } from "solid-element"; import { createSignal, onCleanup } from "solid-js"; import { render } from "solid-js/web"; import { Article } from "./Article"; import { resolvePath } from "./utils/path"; customElement("md-link", {}, (props, { element }) => { noShadowDOM(); const [showArticle, setShowArticle] = createSignal(false); const [expanded, setExpanded] = createSignal(false); let articleContainer: HTMLDivElement | undefined; let disposeArticle: (() => void) | null = null; let articleElement: HTMLElement | null | undefined; // 从 element 的 textContent 获取链接目标(支持 path#section 语法) const rawLinkSrc = element?.textContent?.trim() || ""; // 解析 section(支持 path#section 语法) const hashIndex = rawLinkSrc.indexOf('#'); const path = hashIndex >= 0 ? rawLinkSrc.slice(0, hashIndex) : rawLinkSrc; const section = hashIndex >= 0 ? rawLinkSrc.slice(hashIndex + 1) : undefined; // 隐藏原始文本内容 if (element) { element.textContent = ""; } // 从父节点 article 的 data-src 获取当前 markdown 文件完整路径 const articleEl = element?.closest('article[data-src]'); const articlePath = articleEl?.getAttribute('data-src') || ''; // 解析相对路径 const linkSrc = resolvePath(articlePath, path); // 查找包含此元素的 p 标签 const parentP = element?.closest("p"); const toggleArticle = () => { if (!parentP) return; if (showArticle()) { // 隐藏文章 - 先折叠再移除 setExpanded(false); if (articleContainer) { articleContainer.style.height = '0'; articleContainer.style.opacity = '0'; setTimeout(() => { if (disposeArticle) { disposeArticle(); disposeArticle = null; } articleContainer?.remove(); articleContainer = undefined; articleElement = undefined; }, 300); } setShowArticle(false); } else { // 显示文章 articleContainer = document.createElement("div"); articleContainer.classList.add("md-link-article"); articleContainer.classList.add("ml-4", "border-l-2", "border-gray-200", "pl-4"); articleContainer.style.height = '0'; articleContainer.style.opacity = '0'; articleContainer.style.overflow = 'hidden'; articleContainer.style.transition = 'height 0.3s ease, opacity 0.3s ease'; parentP.after(articleContainer); // 渲染 Article 组件 disposeArticle = render(() => (
{ // 内容加载完成后,获取实际高度并展开 requestAnimationFrame(() => { articleElement = articleContainer?.querySelector('article[data-src]'); if (articleElement) { const height = articleElement.scrollHeight; articleContainer!.style.height = `${height}px`; articleContainer!.style.opacity = '1'; setExpanded(true); } }); }} onError={(err) => console.error("Article error:", err)} /> ), articleContainer); setShowArticle(true); } }; onCleanup(() => { if (disposeArticle) { disposeArticle(); disposeArticle = null; } }); return ( { e.preventDefault(); toggleArticle(); }} class="text-blue-600 hover:text-blue-800 hover:underline" > {section || rawLinkSrc} ); });