Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions src/components/ui/ArticleCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import parse from 'html-react-parser';
import htmlToText from '@site/src/utils/htmlToText';
type ArticleCardProps = {
title: string;
subtitle: string;
Expand Down Expand Up @@ -27,22 +27,18 @@ const PublishDate = ({ date, styles }: { date: string; styles?: string }) => {
</div>
);
};
const Title = (title: string) => {
return <h3 className="text-purple-700">{title}</h3>;
};

function ArticleCard(props: ArticleCardProps) {
// Select fallback image based on index, cycling through available images
const fallbackImage = FALLBACK_IMAGES[(props.index || 0) % FALLBACK_IMAGES.length];

// Sanitizes HTML and converts it to plain text
const sanitizeHtml = (html: string) => {
if (!html) return html;
const div = document.createElement("div");
div.innerHTML = html;
return div.textContent || div.innerText || "";
const subtitleStyle: React.CSSProperties = {
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 3,
overflow: 'hidden',
};
const abbrSubtitle = sanitizeHtml(props.subtitle).trim().split(' ').slice(0, 32).join(' ').concat('...');

const titleContent = htmlToText(props.title);
const subtitleContent = htmlToText(props.subtitle);
if (props.altLayout) {
return (
<article className="my-4 max-w-2xl shadow-lg">
Expand All @@ -54,7 +50,7 @@ function ArticleCard(props: ArticleCardProps) {
href={props.path}
target="_blank"
className="text-white no-underline hover:text-blue-100 hover:no-underline dark:text-white dark:hover:text-blue-50">
{sanitizeHtml(props.title)}
{titleContent}
</a>
</h3>
<PublishDate date={props.date} styles="col-start-1 order-1 row-start-1 z-10" />
Expand All @@ -64,8 +60,8 @@ function ArticleCard(props: ArticleCardProps) {
className=" col-start-1 row-start-1 h-full w-full rounded-sm object-cover lg:w-80"
/>
</div>
<div className="max-w-sm items-center gap-2 self-center p-2 pr-4">
{parse(abbrSubtitle)}
<div className="max-w-sm items-center gap-2 self-center p-2 pr-4" style={subtitleStyle}>
{subtitleContent}
<p className="mt-2 text-purple-700">
By: <a href={props.author_link}>{props.display_name}</a>
</p>
Expand All @@ -84,10 +80,10 @@ function ArticleCard(props: ArticleCardProps) {
href={props.path}
target="_blank"
className="text-white no-underline hover:text-blue-100 hover:no-underline dark:text-white dark:hover:text-blue-50">
{sanitizeHtml(props.title)}
{titleContent}
</a>
</h3>
{parse(abbrSubtitle)}
<div style={subtitleStyle}>{subtitleContent}</div>
<PublishDate date={props.date} styles="row-start-1 col-start-1 z-10 my-2" />
<img src={props.imgSrc || fallbackImage} className="object-fit col-start-1 row-start-1 rounded-sm" />
<p className="text-purple-700">
Expand Down
26 changes: 26 additions & 0 deletions src/utils/htmlToText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import parse from 'html-react-parser';

const collectText = (node: React.ReactNode): string => {
if (typeof node === 'string' || typeof node === 'number') {
return String(node);
}

if (Array.isArray(node)) {
return node.map(collectText).join('');
}

if (React.isValidElement(node)) {
return collectText(node.props.children);
}

return '';
};

export default function htmlToText(html: string): string {
if (!html) {
return '';
}

return collectText(parse(html));
}