Use four-step carousel per program

This commit is contained in:
2026-06-24 09:31:52 +09:00
parent 0c494c9bee
commit 8f5c9051b4

View File

@@ -523,17 +523,23 @@ function FlowRow({
return 'disabled';
};
const isRowClickable = Boolean(onLabelClick) && !isEditing;
const isWrappedStepLayout = steps.length > 5;
const isSingleStepLayout = steps.length === 1;
const stepRows = isWrappedStepLayout
? steps.reduce((rows, step, index) => {
const rowIndex = Math.floor(index / 4);
rows[rowIndex] = [...(rows[rowIndex] ?? []), { step, index }];
return rows;
}, [])
: [];
const wrappedCardColumns = ['xl:col-start-1', 'xl:col-start-3', 'xl:col-start-5', 'xl:col-start-7'];
const wrappedArrowColumns = ['xl:col-start-2', 'xl:col-start-4', 'xl:col-start-6'];
const maxVisibleSteps = 4;
const [stepWindowStart, setStepWindowStart] = useState(0);
const maxWindowStart = Math.max(0, steps.length - maxVisibleSteps);
const visibleStepItems = steps
.map((step, index) => ({ step, index }))
.slice(stepWindowStart, stepWindowStart + maxVisibleSteps);
const placeholderCount = Math.max(0, maxVisibleSteps - visibleStepItems.length);
const canMovePrev = stepWindowStart > 0;
const canMoveNext = stepWindowStart < maxWindowStart;
useEffect(() => {
setStepWindowStart((current) => Math.min(current, maxWindowStart));
}, [maxWindowStart]);
const moveStepWindow = (direction) => {
setStepWindowStart((current) => Math.min(maxWindowStart, Math.max(0, current + direction)));
};
const renderStepCard = (step, index, className) => (
<div className={`relative z-10 flex flex-col gap-2 ${className}`}>
@@ -687,46 +693,59 @@ function FlowRow({
)}
</div>
</div>
{isWrappedStepLayout ? (
<div className="space-y-5">
{stepRows.map((row, rowIndex) => (
<div
key={rowIndex}
className="relative flex flex-col gap-3 xl:grid xl:grid-cols-[minmax(0,1fr)_28px_minmax(0,1fr)_28px_minmax(0,1fr)_28px_minmax(0,1fr)] xl:gap-0 xl:items-stretch"
<div className="relative overflow-hidden rounded-[24px] bg-white/25 px-9 py-1 ring-1 ring-white/70">
{steps.length > maxVisibleSteps && (
<>
<button
type="button"
disabled={!canMovePrev}
onClick={(event) => {
event.stopPropagation();
moveStepWindow(-1);
}}
className="absolute left-2 top-1/2 z-20 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full bg-white/95 text-slate-500 shadow-sm ring-1 ring-slate-200 hover:bg-slate-50 disabled:opacity-25"
aria-label="이전 스텝 보기"
>
{row.map(({ step, index }, itemIndex) => (
<React.Fragment key={step.id}>
{renderStepCard(
step,
index,
`min-w-0 ${wrappedCardColumns[itemIndex]}`
)}
{itemIndex < row.length - 1 && (
<div className={`flex items-center justify-center ${wrappedArrowColumns[itemIndex]}`}>
<ArrowRight className={`hidden h-5 w-5 xl:block ${accent.arrowText}`} />
<ArrowDown className={`h-5 w-5 xl:hidden ${accent.arrowText}`} />
</div>
)}
</React.Fragment>
))}
</button>
<button
type="button"
disabled={!canMoveNext}
onClick={(event) => {
event.stopPropagation();
moveStepWindow(1);
}}
className="absolute right-2 top-1/2 z-20 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full bg-white/95 text-slate-500 shadow-sm ring-1 ring-slate-200 hover:bg-slate-50 disabled:opacity-25"
aria-label="다음 스텝 보기"
>
</button>
</>
)}
<div className="relative grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4 xl:gap-5">
{visibleStepItems.map(({ step, index }, itemIndex) => (
<div key={step.id} className="relative">
{renderStepCard(step, index, 'min-w-0')}
{itemIndex < visibleStepItems.length - 1 && (
<ArrowDown className={`mx-auto mt-2 h-5 w-5 xl:hidden ${accent.arrowText}`} />
)}
</div>
))}
</div>
) : (
<div className="relative flex flex-col gap-3 xl:flex-row xl:items-stretch">
{steps.map((step, index) => (
<React.Fragment key={step.id}>
{renderStepCard(step, index, isSingleStepLayout ? 'w-full max-w-[320px]' : 'min-w-0 flex-1')}
{index < steps.length - 1 && (
<div className="flex items-center justify-center xl:w-7">
<ArrowRight className={`hidden h-5 w-5 xl:block ${accent.arrowText}`} />
<ArrowDown className={`h-5 w-5 xl:hidden ${accent.arrowText}`} />
</div>
)}
</React.Fragment>
{Array.from({ length: placeholderCount }).map((_, index) => (
<div
key={`placeholder-${index}`}
className="hidden min-h-[220px] rounded-2xl border border-dashed border-transparent xl:block"
/>
))}
{visibleStepItems.slice(0, -1).map((item, index) => (
<ArrowRight
key={`arrow-${item.step.id}`}
className={`pointer-events-none absolute top-1/2 z-20 hidden h-5 w-5 -translate-x-1/2 -translate-y-1/2 xl:block ${accent.arrowText}`}
style={{ left: `${25 * (index + 1)}%` }}
/>
))}
</div>
)}
</div>
</section>
);
}