11import React from "react" ;
2+ import Image from "next/image" ;
23import clsx from "clsx" ;
34import { FiExternalLink } from "react-icons/fi" ;
45import styles from "./LinkCard.module.css" ;
56
7+ type LinkCardType = "inline" | "inline-preview" | "preview" ;
8+
69interface ILinkCardProps {
710 href : string ;
8- title : string ;
11+ title ?: string ;
12+ description ?: string ;
13+ image ?: string ;
14+ type ?: LinkCardType ;
915 className ?: string ;
1016}
1117
@@ -30,27 +36,88 @@ function getFaviconUrl(url: string): string {
3036export const LinkCard = function LinkCard ( {
3137 href,
3238 title,
39+ description,
40+ image,
41+ type = "inline-preview" ,
3342 className,
3443} : ILinkCardProps ) {
3544 const domain = getDomain ( href ) ;
3645 const favicon = getFaviconUrl ( href ) ;
3746
47+ // inline: just favicon + domain link
48+ if ( type === "inline" ) {
49+ return (
50+ < a
51+ href = { href }
52+ target = "_blank"
53+ rel = "noopener noreferrer"
54+ className = { clsx ( styles . inline , className ) }
55+ >
56+ { favicon && (
57+ // eslint-disable-next-line @next/next/no-img-element
58+ < img src = { favicon } alt = "" className = { styles . faviconSmall } loading = "lazy" />
59+ ) }
60+ < span className = { styles . inlineDomain } > { domain } </ span >
61+ < FiExternalLink className = { styles . iconSmall } />
62+ </ a >
63+ ) ;
64+ }
65+
66+ // inline-preview: favicon + title + domain (current default)
67+ if ( type === "inline-preview" ) {
68+ return (
69+ < a
70+ href = { href }
71+ target = "_blank"
72+ rel = "noopener noreferrer"
73+ className = { clsx ( styles . card , className ) }
74+ >
75+ { favicon && (
76+ // eslint-disable-next-line @next/next/no-img-element
77+ < img src = { favicon } alt = "" className = { styles . favicon } loading = "lazy" />
78+ ) }
79+ < div className = { styles . content } >
80+ < span className = { styles . title } > { title || domain } </ span >
81+ < span className = { styles . domain } > { domain } </ span >
82+ </ div >
83+ < FiExternalLink className = { styles . icon } />
84+ </ a >
85+ ) ;
86+ }
87+
88+ // preview: full block with OG image, title, description
3889 return (
3990 < a
4091 href = { href }
4192 target = "_blank"
4293 rel = "noopener noreferrer"
43- className = { clsx ( styles . card , className ) }
94+ className = { clsx ( styles . preview , className ) }
4495 >
45- { favicon && (
46- // eslint-disable-next-line @next/next/no-img-element
47- < img src = { favicon } alt = "" className = { styles . favicon } loading = "lazy" />
96+ { image && (
97+ < div className = { styles . imageWrapper } >
98+ < Image
99+ src = { image }
100+ alt = { title || domain }
101+ fill
102+ sizes = "(max-width: 768px) 100vw, 300px"
103+ className = { styles . image }
104+ />
105+ </ div >
48106 ) }
49- < div className = { styles . content } >
50- < span className = { styles . title } > { title } </ span >
51- < span className = { styles . domain } > { domain } </ span >
107+ < div className = { styles . previewContent } >
108+ < div className = { styles . previewHeader } >
109+ { favicon && (
110+ // eslint-disable-next-line @next/next/no-img-element
111+ < img src = { favicon } alt = "" className = { styles . faviconSmall } loading = "lazy" />
112+ ) }
113+ < span className = { styles . previewDomain } > { domain } </ span >
114+ </ div >
115+ < span className = { styles . previewTitle } > { title || domain } </ span >
116+ { description && (
117+ < p className = { styles . previewDescription } > { description } </ p >
118+ ) }
52119 </ div >
53- < FiExternalLink className = { styles . icon } />
120+ < FiExternalLink className = { styles . previewIcon } />
54121 </ a >
55122 ) ;
56123} ;
0 commit comments