logipedia/src/components/editor/extentions/link-preview/link-preview-component.tsx

137 lines
3.6 KiB
TypeScript

"use client";
import React from "react";
import { NodeViewProps, NodeViewWrapper } from "@tiptap/react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ArrowRight } from "lucide-react";
import { Skeleton } from "@/components/ui/skeleton";
export type LinkPreviewData = {
href: string;
title: string;
description: string;
image: string;
};
const InputLink = ({ onSubmit }: { onSubmit: (link: string) => void }) => {
const [link, setLink] = React.useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); // Prevent page reload
if (link.trim()) {
onSubmit(link); // Pass link to parent function
setLink(""); // Clear input field after submission
}
};
const inputRef = React.useRef<HTMLInputElement>(null);
React.useEffect(() => {
inputRef.current?.focus();
}, []);
return (
<form onSubmit={handleSubmit} className="flex gap-2 " autoFocus>
<Input
ref={inputRef}
autoFocus
type="url"
placeholder="Enter a link"
value={link}
onChange={(e) => setLink(e.target.value)}
className="flex-1 focus-visible:ring-transparent border-0"
/>
<Button
type="submit"
variant={"ghost"}
size={"icon"}
className="text-muted-foreground"
>
<ArrowRight />
</Button>
</form>
);
};
const Preview = ({
href,
title,
description: _description,
image,
}: LinkPreviewData) => {
const description =
_description?.length > 100
? `${_description?.slice(0, 150)}...`
: _description;
return (
<a href={href} target="_blank">
<div className=" flex gap-4 flex-col-reverse md:flex-row">
<div className="w-full space-y-2">
<h2
className="text-xl"
style={{
margin: 0,
fontSize: "1.5rem",
}}
>
{title}
</h2>
<p className="text-sm mt-2">{description}</p>
<span className="text-xs text-muted-foreground ">{href}</span>
</div>
{image?.length ? (
<img
src={image}
alt={title}
className="w-full max-w-40 rounded-md object-cover "
/>
) : (
<div className="size-20 rounded-md bg-muted" />
)}
</div>
</a>
);
};
export const LinkPreviewComponent: React.FC<NodeViewProps> = ({
node,
updateAttributes,
extension,
}) => {
const [preview, setPreview] = React.useState<LinkPreviewData | undefined>(
(node.attrs?.href?.length && (node.attrs as LinkPreviewData)) ?? undefined
);
const [loading, setLoading] = React.useState(false);
return (
<NodeViewWrapper as="div" className="p-4 rounded-md bg-background border ">
{loading ? (
<Skeleton className="h-8 w-full rounded" />
) : preview ? (
<Preview {...preview} />
) : (
<InputLink
onSubmit={async (url: string) => {
setLoading(true);
try {
const metadata = await extension.options.fetchMetadata(url);
const newAttrs = {
href: url,
title: metadata?.title,
description: metadata?.description,
image: metadata?.image,
};
updateAttributes(newAttrs);
setPreview(newAttrs);
setLoading(false);
} catch (error) {
console.error("Error fetching metadata:", error);
setLoading(false);
}
}}
/>
)}
</NodeViewWrapper>
);
};