78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
import type { ComponentProps } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const SHOW_REFERENCE_EVENT = "workspace:show-reference";
|
|
|
|
export function CitationLink({
|
|
href,
|
|
children,
|
|
className,
|
|
...props
|
|
}: ComponentProps<"a">) {
|
|
const childrenText = normalizeCitationText(children);
|
|
const displayText = childrenText ?? "";
|
|
const referenceId = href?.startsWith("#reference-")
|
|
? href.slice("#reference-".length)
|
|
: null;
|
|
|
|
if (referenceId) {
|
|
const badgeLabel = displayText.replace(/^\[|\]$/g, "") || referenceId;
|
|
return (
|
|
<a
|
|
href={href}
|
|
title={`查看参考文献 ${badgeLabel}`}
|
|
className={cn(
|
|
"border-primary/30 bg-primary/10 text-primary hover:border-primary/60 hover:bg-primary/20 focus-visible:ring-primary/30 mx-0.5 inline-flex h-[18px] min-w-[18px] -translate-y-[1px] items-center justify-center rounded-full border px-1 text-[10.5px] font-semibold tabular-nums no-underline transition-colors focus-visible:ring-2 focus-visible:outline-none",
|
|
className,
|
|
)}
|
|
onClick={(event) => {
|
|
event.preventDefault();
|
|
const batchId =
|
|
event.currentTarget.closest<HTMLElement>("[data-reference-batch-id]")
|
|
?.dataset.referenceBatchId;
|
|
window.dispatchEvent(
|
|
new CustomEvent(SHOW_REFERENCE_EVENT, {
|
|
detail: { batchId, referenceId },
|
|
}),
|
|
);
|
|
const scroll = () => {
|
|
const target = document.getElementById(`reference-${referenceId}`);
|
|
target?.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
};
|
|
window.setTimeout(scroll, 240);
|
|
}}
|
|
{...props}
|
|
>
|
|
{badgeLabel}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<a
|
|
href={href}
|
|
target={href?.startsWith("http") ? "_blank" : undefined}
|
|
rel={href?.startsWith("http") ? "noopener noreferrer" : undefined}
|
|
className={cn(
|
|
"text-primary decoration-primary/30 hover:decoration-primary/60 underline underline-offset-2 transition-colors",
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{displayText || children}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
function normalizeCitationText(children: ComponentProps<"a">["children"]) {
|
|
if (typeof children !== "string") {
|
|
return null;
|
|
}
|
|
let text = children.trim();
|
|
while (/^citation:\s*/i.test(text)) {
|
|
text = text.replace(/^citation:\s*/i, "");
|
|
}
|
|
return text;
|
|
}
|