"use client"; import React, { useMemo } from "react"; import { ComponentData } from "@/types/screen"; import { FlowVisibilityConfig } from "@/types/control-management"; import { useCurrentFlowStep } from "@/stores/flowStepStore"; interface FlowButtonGroupProps { /** * 그룹에 속한 버튼 컴포넌트들 */ buttons: ComponentData[]; /** * 그룹 설정 (첫 번째 버튼의 설정 사용) */ groupConfig: FlowVisibilityConfig; /** * 버튼 렌더링 함수 */ renderButton: (button: ComponentData, isVisible: boolean) => React.ReactNode; /** * 디자인 모드 여부 */ isDesignMode?: boolean; } /** * FlowButtonGroup 컴포넌트 * * 플로우 단계별로 버튼을 표시/숨기고, auto-compact 모드일 때 * Flexbox로 자동 정렬하는 버튼 그룹 컨테이너입니다. * * **특징:** * - 같은 groupId를 가진 버튼들을 하나의 Flexbox 컨테이너로 묶음 * - 현재 플로우 단계에 따라 버튼을 동적으로 표시/숨김 * - 숨겨진 버튼은 렌더링하지 않아 빈 공간이 자동으로 제거됨 * - 그룹 내 정렬, 간격, 방향을 세밀하게 제어 가능 */ export const FlowButtonGroup: React.FC = ({ buttons, groupConfig, renderButton, isDesignMode = false, }) => { // 현재 플로우 단계 const currentStep = useCurrentFlowStep(groupConfig.targetFlowComponentId); // 각 버튼의 표시 여부 계산 const buttonVisibility = useMemo(() => { return buttons.map((button) => { const config = (button as any).webTypeConfig?.flowVisibilityConfig as FlowVisibilityConfig | undefined; // 플로우 제어 비활성화 시 항상 표시 if (!config?.enabled) { return true; } // 플로우 단계가 선택되지 않은 경우 if (currentStep === null) { // 화이트리스트 모드일 때는 단계 미선택 시 숨김 if (config.mode === "whitelist") { return false; } return true; } const { mode, visibleSteps = [], hiddenSteps = [] } = config; if (mode === "whitelist") { return visibleSteps.includes(currentStep); } else if (mode === "blacklist") { return !hiddenSteps.includes(currentStep); } else if (mode === "all") { return true; } return true; }); }, [buttons, currentStep]); // 표시할 버튼 필터링 const visibleButtons = useMemo(() => { return buttons.filter((_, index) => buttonVisibility[index]); }, [buttons, buttonVisibility]); // 그룹 스타일 계산 const groupStyle: React.CSSProperties = useMemo(() => { const direction = groupConfig.groupDirection || "horizontal"; const gap = groupConfig.groupGap ?? 8; const align = groupConfig.groupAlign || "start"; let justifyContent: string; switch (align) { case "start": justifyContent = "flex-start"; break; case "center": justifyContent = "center"; break; case "end": justifyContent = "flex-end"; break; case "space-between": justifyContent = "space-between"; break; case "space-around": justifyContent = "space-around"; break; default: justifyContent = "flex-start"; } return { display: "flex", flexDirection: direction === "vertical" ? "column" : "row", gap: `${gap}px`, justifyContent, alignItems: "center", flexWrap: "wrap", // 넘칠 경우 줄바꿈 width: "100%", // 🆕 전체 너비를 차지하도록 설정 (끝점/중앙 정렬을 위해 필수) }; }, [groupConfig]); // 디자인 모드에서는 모든 버튼 표시 (반투명 처리) if (isDesignMode) { return (
{buttons.map((button, index) => (
{renderButton(button, buttonVisibility[index])} {!buttonVisibility[index] && (
숨김
)}
))}
); } // 실제 뷰 모드: 보이는 버튼만 렌더링 (auto-compact 동작) return (
{visibleButtons.map((button) => (
{renderButton(button, true)}
))}
); };