Import source: app/src/widgets/ArticleCardWidget

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:26:00 +03:30
parent 1181e50e0d
commit 11c4d631a9
+93
View File
@@ -0,0 +1,93 @@
// Embeddable article preview card.
import { useEffect, useRef } from 'react';
import { TeknavData } from '../../teknav-data.js';
const SITE_URL = 'https://www.teknav.ir';
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
const cardStyle = {
fontFamily: '\'Vazirmatn\', Tahoma, sans-serif',
direction: 'rtl',
maxWidth: 480,
border: isDark ? '1px solid rgba(255,255,255,0.1)' : '1px solid #E4DDD2',
borderRadius: 12,
overflow: 'hidden',
background: isDark ? '#0C1E22' : '#fff',
};
export function ArticleCardWidget({ slug }) {
const article = TeknavData.articles.find((a) => a.slug === slug);
const lastHeight = useRef(0);
useEffect(() => {
const el = document.getElementById('widget-root');
if (!el) return;
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const h = entry.target.offsetHeight || 0;
if (h !== lastHeight.current) {
lastHeight.current = h;
window.parent?.postMessage({ type: 'resize', height: h }, '*');
}
}
});
observer.observe(el);
return () => observer.disconnect();
}, []);
if (!article) {
return (
<div style={{ ...cardStyle, padding: 20, color: isDark ? 'rgba(255,255,255,0.5)' : '#5F6B6D', fontSize: 14 }}>
مقاله یافت نشد.
</div>
);
}
const image = article.ogImage || `/images/og/${slug}.jpg`;
return (
<div style={cardStyle}>
<div style={{ height: 180, overflow: 'hidden', position: 'relative' }}>
<img
src={`${SITE_URL}${image}`}
alt={article.title}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
loading="lazy"
/>
<div style={{ position: 'absolute', bottom: 8, right: 12 }}>
<span style={{ background: '#0F6B73', color: '#fff', fontSize: 11, fontWeight: 700, padding: '3px 10px', borderRadius: 20 }}>
{article.categoryName || ''}
</span>
</div>
<div style={{ position: 'absolute', top: 8, left: 12 }}>
<span style={{ background: 'rgba(0,0,0,0.55)', color: '#fff', fontSize: 11, padding: '2px 8px', borderRadius: 20 }}>
{article.readTime} دقیقه
</span>
</div>
</div>
<div style={{ padding: '16px 20px' }}>
<h3 style={{ fontSize: 16, fontWeight: 700, color: isDark ? '#fff' : '#0C1E22', lineHeight: 1.6, margin: '0 0 8px' }}>{article.title}</h3>
<p style={{ fontSize: 13, color: isDark ? 'rgba(255,255,255,0.6)' : '#5F6B6D', lineHeight: 1.8, margin: 0 }}>
{String(article.summary || '').slice(0, 120)}…
</p>
</div>
<div style={{
borderTop: isDark ? '1px solid rgba(255,255,255,0.08)' : '1px solid #E4DDD2',
padding: '10px 20px',
background: isDark ? '#061014' : '#F8F5F0',
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
}}>
<span style={{ fontSize: 12, color: isDark ? 'rgba(255,255,255,0.4)' : '#5F6B6D' }}>{article.authorName}</span>
<a
href={`${SITE_URL}/article/${slug}`}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: 12, color: '#D49A2A', textDecoration: 'none', fontWeight: 600 }}
>
خواندن در تکناو ↗
</a>
</div>
</div>
);
}