ERP-node/frontend/components/screen/widgets/FlowButtonGroup.tsx

168 lines
4.8 KiB
TypeScript

"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<FlowButtonGroupProps> = ({
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 (
<div style={groupStyle} className="flow-button-group">
{buttons.map((button, index) => (
<div
key={button.id}
style={{
opacity: buttonVisibility[index] ? 1 : 0.3,
position: "relative",
}}
>
{renderButton(button, buttonVisibility[index])}
{!buttonVisibility[index] && (
<div
className="pointer-events-none absolute inset-0 flex items-center justify-center bg-gray-900/10"
style={{
border: "1px dashed #94a3b8",
}}
>
<span className="text-[10px] text-gray-500"></span>
</div>
)}
</div>
))}
</div>
);
}
// 실제 뷰 모드: 보이는 버튼만 렌더링 (auto-compact 동작)
return (
<div style={groupStyle} className="flow-button-group">
{visibleButtons.map((button) => (
<div key={button.id} style={{ position: "relative" }}>
{renderButton(button, true)}
</div>
))}
</div>
);
};