feat: bouton Personnaliser pour période personnalisée (1–365 jours)
ci/woodpecker/push/woodpecker Pipeline was successful
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>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
|
||||
interface PeriodSelectorProps {
|
||||
value: number;
|
||||
onChange: (days: number) => void;
|
||||
@@ -11,16 +13,40 @@ const PERIODS = [
|
||||
{ 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">
|
||||
<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)}
|
||||
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
|
||||
${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]'
|
||||
}
|
||||
@@ -29,7 +55,46 @@ export function PeriodSelector({ value, onChange, animationActive, onAnimate }:
|
||||
{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={`
|
||||
|
||||
Reference in New Issue
Block a user