Files
n3wt-school/Front-End/src/components/ProgressStep.js
2025-04-15 19:41:42 +02:00

187 lines
5.7 KiB
JavaScript

import React, { useState, useEffect } from 'react';
const Step = ({ number, title, isActive, isValid, isCompleted, onClick }) => {
return (
<div className="flex-shrink-0 flex justify-center relative mx-4">
<div
className={`
w-8 h-8 rounded-full
flex items-center justify-center
text-sm font-semibold
${
isCompleted
? 'bg-emerald-600 text-white'
: isActive
? 'bg-emerald-600 text-white'
: 'bg-gray-200 text-gray-600'
}
`}
>
{isCompleted ? (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
) : (
number
)}
</div>
<div className="absolute top-12 left-1/2 -translate-x-1/2">
<span
className={`
text-xs font-medium w-20 text-center block break-words
${isActive ? 'text-emerald-600' : 'text-gray-500'}
`}
>
{title}
</span>
</div>
</div>
);
};
const SpacerStep = ({ isCompleted }) => {
return (
<div
className={`flex-1 h-0.5 ${isCompleted ? 'bg-emerald-600' : 'bg-gray-200'}`}
/>
);
};
const Dots = () => {
return (
<div className="text-gray-500 relative flex items-center mx-4">
<span>...</span>
<div className="absolute top-8 left-1/2 -translate-x-1/2">
<span className="text-xs font-medium w-20 text-center block">...</span>
</div>
</div>
);
};
const ProgressStep = ({
steps,
stepTitles,
currentStep,
setStep,
isStepValid,
}) => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const [visibleSteps, setVisibleSteps] = useState(steps);
useEffect(() => {
const handleResize = () => setWindowWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(() => {
const calculateVisibleSteps = () => {
const minWidth = 150; // Largeur minimale estimée par étape
const maxVisibleSteps = Math.floor(windowWidth / minWidth);
if (maxVisibleSteps >= steps.length) {
setVisibleSteps(steps);
return;
}
if (maxVisibleSteps < 4) {
// Garder seulement première, dernière et courante
let filtered = [steps[0]];
if (currentStep > 1 && currentStep < steps.length) {
filtered.push('...');
filtered.push(steps[currentStep - 1]);
}
if (currentStep < steps.length) {
filtered.push('...');
}
filtered.push(steps[steps.length - 1]);
setVisibleSteps(filtered);
} else {
// Garder première, dernière, courante et quelques étapes adjacentes
let filtered = [steps[0]];
if (currentStep > 2) filtered.push('...');
if (currentStep > 1 && currentStep < steps.length) {
filtered.push(steps[currentStep - 1]);
}
if (currentStep < steps.length - 1) filtered.push('...');
filtered.push(steps[steps.length - 1]);
setVisibleSteps(filtered);
}
};
calculateVisibleSteps();
}, [windowWidth, currentStep, steps]);
const handleStepClick = (stepIndex) => {
// Vérifie si on peut naviguer vers l'étape (toutes les étapes précédentes doivent être valides)
const canNavigate = Array.from(
{ length: stepIndex },
(_, i) => i + 1
).every((step) => isStepValid(step));
if (canNavigate) {
setStep(stepIndex + 1);
}
};
return (
<div className="w-full py-6">
<div className="flex items-center min-h-[100px]">
{visibleSteps.map((step, index) => {
if (step === '...') {
return (
<div
key={`dots-${index}`}
className="flex-1 flex items-center justify-center"
>
<Dots />
{index !== visibleSteps.length - 1 && (
<SpacerStep isCompleted={false} />
)}
</div>
);
}
const originalIndex = steps.indexOf(step);
return (
<div
key={index}
className={`
flex-1 relative
${Array.from({ length: originalIndex + 1 }, (_, i) => i + 1).every((s) => isStepValid(s)) ? 'cursor-pointer' : 'cursor-not-allowed'}
`}
onClick={() => handleStepClick(originalIndex)}
>
<div className="flex items-center">
<div className="w-full flex items-center">
<Step
number={originalIndex + 1}
title={stepTitles ? stepTitles[originalIndex + 1] : step}
isActive={currentStep === originalIndex + 1}
isCompleted={currentStep > originalIndex + 1}
isValid={isStepValid(originalIndex + 1)}
/>
{index !== visibleSteps.length - 1 && (
<SpacerStep isCompleted={currentStep > originalIndex + 1} />
)}
</div>
</div>
</div>
);
})}
</div>
</div>
);
};
export default ProgressStep;