Import source: app/src/widgets/GlossaryWidget
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
// Embeddable glossary definition card.
|
||||
// Rendered in an iframe; sends postMessage resize to parent only when height changes.
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { TeknavData } from '../../teknav-data.js';
|
||||
|
||||
const SITE_URL = 'https://www.teknav.ir';
|
||||
|
||||
const light = {
|
||||
card: { background: '#fff', border: '1px solid #E4DDD2', color: '#263238' },
|
||||
header: { background: '#0C1E22' },
|
||||
footer: { background: '#F8F5F0', borderTop: '1px solid #E4DDD2' },
|
||||
summary: { color: '#263238' },
|
||||
faqQ: { color: '#0F6B73' },
|
||||
faqA: { color: '#5F6B6D' },
|
||||
link: { color: '#0F6B73' },
|
||||
};
|
||||
const dark = {
|
||||
card: { background: '#0C1E22', border: '1px solid rgba(255,255,255,0.1)', color: '#E4DDD2' },
|
||||
header: { background: '#061014' },
|
||||
footer: { background: '#061014', borderTop: '1px solid rgba(255,255,255,0.08)' },
|
||||
summary: { color: 'rgba(255,255,255,0.85)' },
|
||||
faqQ: { color: '#D49A2A' },
|
||||
faqA: { color: 'rgba(255,255,255,0.6)' },
|
||||
link: { color: '#D49A2A' },
|
||||
};
|
||||
|
||||
function useTheme() {
|
||||
const darkMq = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)');
|
||||
return darkMq?.matches ? dark : light;
|
||||
}
|
||||
|
||||
export function GlossaryWidget({ slug }) {
|
||||
const term = (TeknavData.glossary ?? []).find((g) => g.slug === slug);
|
||||
const theme = useTheme();
|
||||
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();
|
||||
}, []);
|
||||
|
||||
const cardBase = {
|
||||
fontFamily: '\'Vazirmatn\', Tahoma, sans-serif',
|
||||
direction: 'rtl',
|
||||
maxWidth: 480,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
...theme.card,
|
||||
};
|
||||
|
||||
if (!term) {
|
||||
return (
|
||||
<div style={{ ...cardBase, padding: 20, fontSize: 14 }}>
|
||||
اصطلاح یافت نشد.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={cardBase}>
|
||||
<div style={{ ...theme.header, padding: '16px 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ color: '#D49A2A', fontWeight: 700, fontSize: 16 }}>{term.term}</span>
|
||||
<span style={{ color: 'rgba(255,255,255,0.5)', fontSize: 12 }}>{term.english}</span>
|
||||
</div>
|
||||
<div style={{ padding: '16px 20px' }}>
|
||||
<p style={{ fontSize: 14, lineHeight: 1.9, margin: 0, ...theme.summary }}>{term.summary}</p>
|
||||
{(term.faqs || []).length > 0 && (
|
||||
<div style={{ marginTop: 12 }}>
|
||||
{term.faqs.map(([q, a], i) => (
|
||||
<details key={i} style={{ marginBottom: 8, fontSize: 13 }}>
|
||||
<summary style={{ fontWeight: 600, cursor: 'pointer', ...theme.faqQ }}>{q}</summary>
|
||||
<p style={{ lineHeight: 1.8, marginTop: 6, paddingRight: 12, ...theme.faqA }}>{a}</p>
|
||||
</details>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ padding: '10px 20px', ...theme.footer }}>
|
||||
<a
|
||||
href={`${SITE_URL}/glossary/${slug}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ fontSize: 12, textDecoration: 'none', fontWeight: 600, ...theme.link }}
|
||||
>
|
||||
منبع: تکناو ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user