mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
refactor: Changement des IconTextInput en TextInput, modification du composant step
This commit is contained in:
157
Front-End/src/components/ProgressStep.js
Normal file
157
Front-End/src/components/ProgressStep.js
Normal file
@ -0,0 +1,157 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user