fix: VIX slider bar overflow — clamp pivot to range, add center prop
Slider was computing mid = (0 - 10) / (60 - 10) * 100 = -20% for VIX (0 outside the 10-60 range), causing the colored track to start at a negative left position and overflow the container. Fix: add overflow-hidden on the track div; add center prop (defaults 0) that the caller sets to the actual baseline value when 0 is out of range. VIX now pivots at 18, real yield US at 2.1. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -130,21 +130,25 @@ function compute(p: Params): ModelResult {
|
|||||||
|
|
||||||
function Slider({
|
function Slider({
|
||||||
label, value, min, max, step, format, onChange,
|
label, value, min, max, step, format, onChange,
|
||||||
colorize = false, reverse = false,
|
colorize = false, reverse = false, center,
|
||||||
}: {
|
}: {
|
||||||
label: string; value: number; min: number; max: number; step: number
|
label: string; value: number; min: number; max: number; step: number
|
||||||
format: (v: number) => string
|
format: (v: number) => string
|
||||||
onChange: (v: number) => void
|
onChange: (v: number) => void
|
||||||
colorize?: boolean
|
colorize?: boolean
|
||||||
reverse?: boolean // true = value goes up → negative (red)
|
reverse?: boolean // true = value goes up → negative (red)
|
||||||
|
center?: number // neutral pivot point (defaults to 0; use baseline value if 0 is out of range)
|
||||||
}) {
|
}) {
|
||||||
const pct = ((value - min) / (max - min)) * 100
|
const pct = ((value - min) / (max - min)) * 100
|
||||||
const mid = ((0 - min) / (max - min)) * 100
|
const pivot = center ?? 0
|
||||||
|
// Clamp mid to [0,100] so the track never overflows the container
|
||||||
|
const mid = Math.max(0, Math.min(100, ((pivot - min) / (max - min)) * 100))
|
||||||
|
|
||||||
|
const atCenter = Math.abs(value - pivot) < step / 2
|
||||||
let trackColor = 'bg-blue-500'
|
let trackColor = 'bg-blue-500'
|
||||||
if (colorize) {
|
if (colorize) {
|
||||||
const isPositive = reverse ? value < 0 : value > 0
|
const isPositive = reverse ? value < pivot : value > pivot
|
||||||
trackColor = value === 0 ? 'bg-slate-600' : isPositive ? 'bg-emerald-500' : 'bg-rose-500'
|
trackColor = atCenter ? 'bg-slate-600' : isPositive ? 'bg-emerald-500' : 'bg-rose-500'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -153,12 +157,12 @@ function Slider({
|
|||||||
<span className="text-xs text-slate-400">{label}</span>
|
<span className="text-xs text-slate-400">{label}</span>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
'text-xs font-mono font-semibold',
|
'text-xs font-mono font-semibold',
|
||||||
colorize && value !== 0
|
colorize && !atCenter
|
||||||
? (reverse ? value < 0 : value > 0) ? 'text-emerald-400' : 'text-rose-400'
|
? (reverse ? value < pivot : value > pivot) ? 'text-emerald-400' : 'text-rose-400'
|
||||||
: 'text-white',
|
: 'text-white',
|
||||||
)}>{format(value)}</span>
|
)}>{format(value)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative h-1.5 bg-dark-900 rounded">
|
<div className="relative h-1.5 bg-dark-900 rounded overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className={clsx('absolute h-full rounded transition-all', trackColor)}
|
className={clsx('absolute h-full rounded transition-all', trackColor)}
|
||||||
style={colorize
|
style={colorize
|
||||||
@@ -407,12 +411,12 @@ export default function EuroSimulator() {
|
|||||||
<Section title="📊 Marchés & sentiment" color="border-slate-700/30 bg-dark-800/60">
|
<Section title="📊 Marchés & sentiment" color="border-slate-700/30 bg-dark-800/60">
|
||||||
<Slider label="VIX" value={p.vix} min={10} max={60} step={0.5}
|
<Slider label="VIX" value={p.vix} min={10} max={60} step={0.5}
|
||||||
format={v => v.toFixed(1)} onChange={v => set('vix', v)}
|
format={v => v.toFixed(1)} onChange={v => set('vix', v)}
|
||||||
colorize reverse />
|
colorize reverse center={BASE.vix} />
|
||||||
<Slider label="Pétrole (USD/bbl)" value={p.oil} min={40} max={130} step={1}
|
<Slider label="Pétrole (USD/bbl)" value={p.oil} min={40} max={130} step={1}
|
||||||
format={v => `$${v.toFixed(0)}`} onChange={v => set('oil', v)} />
|
format={v => `$${v.toFixed(0)}`} onChange={v => set('oil', v)} />
|
||||||
<Slider label="Taux réel US 10Y" value={p.real_yield_us} min={-1} max={4} step={0.05}
|
<Slider label="Taux réel US 10Y" value={p.real_yield_us} min={-1} max={4} step={0.05}
|
||||||
format={v => `${v > 0 ? '+' : ''}${v.toFixed(2)}%`}
|
format={v => `${v > 0 ? '+' : ''}${v.toFixed(2)}%`}
|
||||||
onChange={v => set('real_yield_us', v)} colorize reverse />
|
onChange={v => set('real_yield_us', v)} colorize reverse center={BASE.real_yield_us} />
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user