a2fdad46d4
ci/woodpecker/push/woodpecker Pipeline was successful
Clic sur "Personnaliser" → champ inline focusé, pré-rempli si déjà custom. Valider avec Entrée ou blur, annuler avec Échap. Plage 1–365 jours. Le bouton affiche la valeur courante (ex. "14 jours") quand une période custom est active, et reprend la surbrillance dorée comme les autres boutons. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
|
|
interface PeriodSelectorProps {
|
|
value: number;
|
|
onChange: (days: number) => void;
|
|
animationActive: boolean;
|
|
onAnimate: () => void;
|
|
}
|
|
|
|
const PERIODS = [
|
|
{ label: '24h', days: 1 },
|
|
{ label: '7 jours', days: 7 },
|
|
{ label: '30 jours', days: 30 },
|
|
];
|
|
|
|
const PRESET_DAYS = new Set([1, 7, 30]);
|
|
|
|
export function PeriodSelector({ value, onChange, animationActive, onAnimate }: PeriodSelectorProps) {
|
|
const [customOpen, setCustomOpen] = useState(false);
|
|
const [inputVal, setInputVal] = useState('');
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
|
|
// Ouvre le champ custom avec la valeur courante pré-remplie
|
|
const openCustom = () => {
|
|
setInputVal(PRESET_DAYS.has(value) ? '' : String(value));
|
|
setCustomOpen(true);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (customOpen) inputRef.current?.focus();
|
|
}, [customOpen]);
|
|
|
|
const commit = () => {
|
|
const n = parseInt(inputVal, 10);
|
|
if (n >= 1 && n <= 365) onChange(n);
|
|
setCustomOpen(false);
|
|
};
|
|
|
|
const isCustomActive = !PRESET_DAYS.has(value);
|
|
|
|
return (
|
|
<div className="flex gap-1 bg-[#0f1016] border border-[#2e2f3a] rounded-lg p-1 items-center">
|
|
{PERIODS.map(({ label, days }) => (
|
|
<button
|
|
key={days}
|
|
onClick={() => { onChange(days); setCustomOpen(false); }}
|
|
className={`
|
|
px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer
|
|
${value === days && !customOpen
|
|
? 'bg-[#d4a843] text-[#0a0b0f] shadow-[0_0_12px_rgba(212,168,67,0.4)]'
|
|
: 'text-[#6b7280] hover:text-[#d4a843] hover:bg-[#1a1b23]'
|
|
}
|
|
`}
|
|
>
|
|
{label}
|
|
</button>
|
|
))}
|
|
|
|
<div className="w-px mx-1 bg-[#2e2f3a] self-stretch" />
|
|
|
|
{/* Bouton Personnaliser + champ inline */}
|
|
{customOpen ? (
|
|
<div className="flex items-center gap-1">
|
|
<input
|
|
ref={inputRef}
|
|
type="number"
|
|
min={1}
|
|
max={365}
|
|
value={inputVal}
|
|
onChange={(e) => setInputVal(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter') commit();
|
|
if (e.key === 'Escape') setCustomOpen(false);
|
|
}}
|
|
onBlur={commit}
|
|
placeholder="jours"
|
|
className="w-16 px-2 py-1 text-sm bg-[#1a1b23] border border-[#d4a843] rounded-md text-[#d4a843] text-center focus:outline-none tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
|
|
/>
|
|
<span className="text-[#6b7280] text-xs">j</span>
|
|
</div>
|
|
) : (
|
|
<button
|
|
onClick={openCustom}
|
|
className={`
|
|
px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer
|
|
${isCustomActive
|
|
? 'bg-[#d4a843] text-[#0a0b0f] shadow-[0_0_12px_rgba(212,168,67,0.4)]'
|
|
: 'text-[#6b7280] hover:text-[#d4a843] hover:bg-[#1a1b23]'
|
|
}
|
|
`}
|
|
>
|
|
{isCustomActive ? `${value} jours` : 'Personnaliser'}
|
|
</button>
|
|
)}
|
|
|
|
<div className="w-px mx-1 bg-[#2e2f3a] self-stretch" />
|
|
|
|
<button
|
|
onClick={onAnimate}
|
|
className={`
|
|
px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer
|
|
${animationActive
|
|
? 'bg-[#d4a843] text-[#0a0b0f] shadow-[0_0_12px_rgba(212,168,67,0.4)]'
|
|
: 'text-[#6b7280] hover:text-[#d4a843] hover:bg-[#1a1b23]'
|
|
}
|
|
`}
|
|
>
|
|
▶ Animer
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|