63 lines
1.5 KiB
TypeScript
63 lines
1.5 KiB
TypeScript
"use client";
|
||
|
||
import type { ReactNode } from "react";
|
||
import { usePostLinkNewTab } from "@/components/PostLinkProvider";
|
||
import { linkifyParts } from "@/lib/linkify";
|
||
|
||
function AutolinkAnchor({
|
||
href,
|
||
children,
|
||
openInNewTab,
|
||
}: {
|
||
href: string;
|
||
children: ReactNode;
|
||
openInNewTab: boolean;
|
||
}) {
|
||
return (
|
||
<a
|
||
href={href}
|
||
className="j13-autolink break-all"
|
||
style={{ color: "var(--accent)", textDecoration: "underline" }}
|
||
{...(openInNewTab ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
||
onClick={(e) => e.stopPropagation()}
|
||
onContextMenu={(e) => e.stopPropagation()}
|
||
>
|
||
{children}
|
||
</a>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 纯文本自动识别 URL。
|
||
* forceNewTab:聊天等场景始终新开;否则读 PostLinkProvider(post_link_new_tab)。
|
||
*/
|
||
export default function AutolinkText({
|
||
text,
|
||
renderText,
|
||
forceNewTab = false,
|
||
}: {
|
||
text: string;
|
||
renderText?: (segment: string) => ReactNode;
|
||
forceNewTab?: boolean;
|
||
}) {
|
||
const settingNewTab = usePostLinkNewTab();
|
||
const openInNewTab = forceNewTab || settingNewTab;
|
||
const parts = linkifyParts(text);
|
||
return (
|
||
<>
|
||
{parts.map((p, i) => {
|
||
const key = `l-${i}`;
|
||
if (p.type === "url") {
|
||
return (
|
||
<AutolinkAnchor key={key} href={p.href} openInNewTab={openInNewTab}>
|
||
{p.value}
|
||
</AutolinkAnchor>
|
||
);
|
||
}
|
||
if (renderText) return <span key={key}>{renderText(p.value)}</span>;
|
||
return <span key={key}>{p.value}</span>;
|
||
})}
|
||
</>
|
||
);
|
||
}
|