Risk slider: mobile 1-card + RiskCard component + padding tweaks

This commit is contained in:
alireza 2026-06-01 10:55:50 +03:30
parent 3c40a655de
commit 54c4aa698b
2 changed files with 98 additions and 70 deletions

View File

@ -161,6 +161,65 @@ const T = {
}, },
} }
/* ─── Shared card ───────────────────────────────────────── */
function RiskCard({
item, cfg, lang, idx, mobile,
}: {
item: RiskItem
cfg: { dot: string; label: string; bg: string }
lang: 'fa' | 'en'
idx: number
mobile?: boolean
}) {
const text = lang === 'en' && item.quoteEn ? item.quoteEn : item.quote
const name = lang === 'en' && item.nameEn ? item.nameEn : item.name
const date = lang === 'en' && item.dateEn ? item.dateEn : item.date
return (
<div
style={{
background: 'var(--paper)',
border: '1px solid var(--rule-thin)',
borderTop: `3px solid ${cfg.dot}`,
padding: mobile ? '20px 18px 16px' : '24px 24px 20px',
direction: 'rtl',
display: 'flex',
flexDirection: 'column',
animation: `riskCardIn 320ms ${idx * 60}ms ease both`,
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 10 }}>
<div style={{ width: 7, height: 7, borderRadius: '50%', background: cfg.dot, flexShrink: 0 }} />
<span style={{ fontSize: 9, fontWeight: 700, letterSpacing: '2px', textTransform: 'uppercase', color: cfg.dot }}>
{cfg.label}
</span>
</div>
<p style={{
fontSize: mobile ? 13 : 13,
lineHeight: 1.8,
color: 'var(--ink-2)',
marginBottom: 14,
flex: 1,
/* on mobile truncate to ~4 lines */
...(mobile ? { WebkitLineClamp: 4, WebkitBoxOrient: 'vertical', overflow: 'hidden', display: '-webkit-box' } : {}),
}}>
{text}
</p>
<div style={{ paddingTop: 12, borderTop: '1px solid var(--rule-thin)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink)' }}>{name}</div>
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>{date}</div>
</div>
<div style={{ fontSize: 9, fontWeight: 700, letterSpacing: '1px', textTransform: 'uppercase', color: cfg.dot, background: cfg.bg, padding: '4px 10px' }}>
{cfg.label}
</div>
</div>
</div>
)
}
/* ─── Section ────────────────────────────────────────────── */ /* ─── Section ────────────────────────────────────────────── */
export default function RiskSection() { export default function RiskSection() {
const { lang } = useLang() const { lang } = useLang()
@ -252,7 +311,7 @@ export default function RiskSection() {
{/* ── Slider ── */} {/* ── Slider ── */}
<div <div
className="max-w-7xl mx-auto px-12 max-md:px-5" className="max-w-7xl mx-auto px-12 max-md:px-5"
style={{ padding: '28px 48px 48px', position: 'relative' }} style={{ paddingTop: 28, paddingBottom: 48, position: 'relative' }}
onMouseEnter={() => setPaused(true)} onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)} onMouseLeave={() => setPaused(false)}
onTouchStart={e => { touchStartX.current = e.touches[0].clientX }} onTouchStart={e => { touchStartX.current = e.touches[0].clientX }}
@ -261,81 +320,50 @@ export default function RiskSection() {
if (Math.abs(dx) > 50) go(dx > 0 ? 'next' : 'prev') if (Math.abs(dx) > 50) go(dx > 0 ? 'next' : 'prev')
}} }}
> >
{/* Cards row */} {/* Desktop: 3 cards */}
<div <div className="hidden md:grid md:grid-cols-3 gap-4">
style={{ {visibleItems.map((item, idx) => (
display: 'grid', <RiskCard key={`${current}-${idx}`} item={item} cfg={levelCfg[item.level]} lang={lang} idx={idx} />
gridTemplateColumns: 'repeat(3, 1fr)', ))}
gap: 16, </div>
overflow: 'hidden', {/* Mobile: 1 card */}
}} <div className="md:hidden">
className="max-md:grid-cols-1" {/* Mobile: single card with slide animation */}
> <div key={current} style={{ animation: 'riskCardIn 320ms ease both' }}>
{visibleItems.map((item, idx) => { <RiskCard item={riskItems[current]} cfg={levelCfg[riskItems[current].level]} lang={lang} idx={0} mobile />
const cfg = levelCfg[item.level] </div>
return (
<div
key={`${current}-${idx}`}
className={idx > 0 ? 'max-md:hidden' : ''}
style={{
background: 'var(--paper)',
border: '1px solid var(--rule-thin)',
borderTop: `3px solid ${cfg.dot}`,
padding: '24px 24px 20px',
direction: 'rtl',
display: 'flex',
flexDirection: 'column',
animation: `riskCardIn 320ms ease both`,
animationDelay: `${idx * 60}ms`,
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 12 }}>
<div style={{ width: 7, height: 7, borderRadius: '50%', background: cfg.dot, flexShrink: 0 }} />
<span style={{ fontSize: 9, fontWeight: 700, letterSpacing: '2px', textTransform: 'uppercase', color: cfg.dot }}>
{cfg.label}
</span>
</div>
<p style={{ fontSize: 13, lineHeight: 1.85, color: 'var(--ink-2)', fontWeight: 400, marginBottom: 16, flex: 1 }}>
{lang === 'en' && item.quoteEn ? item.quoteEn : item.quote}
</p>
<div style={{ paddingTop: 12, borderTop: '1px solid var(--rule-thin)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink)' }}>{lang === 'en' && item.nameEn ? item.nameEn : item.name}</div>
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>{lang === 'en' && item.dateEn ? item.dateEn : item.date}</div>
</div>
<div style={{ fontSize: 9, fontWeight: 700, letterSpacing: '1px', textTransform: 'uppercase', color: cfg.dot, background: cfg.bg, padding: '4px 10px' }}>
{cfg.label}
</div>
</div>
</div>
)
})}
</div> </div>
{/* Controls */} {/* Progress bar (mobile) + dots (desktop) + arrows */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 24 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 20, gap: 12 }}>
{/* Dots */} {/* Progress bar on mobile, dots on desktop */}
<div style={{ display: 'flex', gap: 6 }}> <div className="md:hidden" style={{ flex: 1, height: 3, background: 'var(--rule-thin)', borderRadius: 2 }}>
<div style={{
height: '100%', borderRadius: 2,
background: 'var(--ink)',
width: `${((current + 1) / total) * 100}%`,
transition: 'width 300ms ease',
}} />
</div>
<div className="hidden md:flex" style={{ display: 'none', gap: 6 }}>
{riskItems.map((_, i) => ( {riskItems.map((_, i) => (
<button <button key={i} onClick={() => setCurrent(i)} style={{
key={i} width: i === current ? 24 : 8, height: 8, borderRadius: 4,
onClick={() => { setAnimDir('next'); setCurrent(i) }} border: 'none', cursor: 'pointer', padding: 0,
style={{ background: i === current ? 'var(--ink)' : 'var(--rule-thin)',
width: i === current ? 24 : 8, height: 8, transition: 'all 300ms ease',
borderRadius: 4, border: 'none', cursor: 'pointer', }} />
background: i === current ? 'var(--ink)' : 'var(--rule-thin)',
transition: 'all 300ms ease', padding: 0,
}}
/>
))} ))}
</div> </div>
{/* Counter on mobile */}
<span className="md:hidden" style={{ fontSize: 12, color: 'var(--ink-4)', flexShrink: 0 }}>
{(current + 1).toLocaleString('fa-IR')} / {total.toLocaleString('fa-IR')}
</span>
{/* Arrows */} {/* Arrows */}
<div style={{ display: 'flex', gap: 8 }}> <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
<button <button onClick={() => go('prev')}
onClick={() => go('prev')}
style={{ width: 36, height: 36, border: '1px solid var(--rule-thin)', background: 'transparent', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 150ms' }} style={{ width: 36, height: 36, border: '1px solid var(--rule-thin)', background: 'transparent', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 150ms' }}
onMouseEnter={e => (e.currentTarget.style.background = 'var(--paper-2)')} onMouseEnter={e => (e.currentTarget.style.background = 'var(--paper-2)')}
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')} onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}

View File

@ -29,7 +29,7 @@ export default function SnapshotBar() {
<div <div
key={price.id} key={price.id}
style={{ style={{
padding: '24px 0', padding: '28px 0',
borderRight: isLast ? 'none' : '1px solid var(--rule-thin)', borderRight: isLast ? 'none' : '1px solid var(--rule-thin)',
paddingLeft: idx === 0 ? '0' : '28px', paddingLeft: idx === 0 ? '0' : '28px',
paddingRight: idx === prices.length - 1 ? '0' : '28px', paddingRight: idx === prices.length - 1 ? '0' : '28px',