From c78326bae1bbfe1e08d1a79b600aec08f6835493 Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 30 Dec 2025 14:11:42 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=B9=84=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config-panels/ButtonConfigPanel.tsx | 73 ++++++++++++++ .../button-primary/ButtonPrimaryComponent.tsx | 99 +++++++++++++++++-- 2 files changed, 165 insertions(+), 7 deletions(-) diff --git a/frontend/components/screen/config-panels/ButtonConfigPanel.tsx b/frontend/components/screen/config-panels/ButtonConfigPanel.tsx index 3a126c29..417ea4ff 100644 --- a/frontend/components/screen/config-panels/ButtonConfigPanel.tsx +++ b/frontend/components/screen/config-panels/ButtonConfigPanel.tsx @@ -3081,6 +3081,79 @@ export const ButtonConfigPanel: React.FC = ({ /> )} + {/* πŸ†• ν–‰ 선택 μ‹œμ—λ§Œ ν™œμ„±ν™” μ„€μ • */} +
+

ν–‰ 선택 ν™œμ„±ν™” 쑰건

+

+ ν…Œμ΄λΈ” λ¦¬μŠ€νŠΈλ‚˜ λΆ„ν•  νŒ¨λ„μ—μ„œ 데이터가 μ„ νƒλ˜μ—ˆμ„ λ•Œλ§Œ λ²„νŠΌμ„ ν™œμ„±ν™”ν•©λ‹ˆλ‹€. +

+ +
+
+ +

+ μ²΄ν¬ν•˜λ©΄ ν…Œμ΄λΈ”μ—μ„œ 행을 μ„ νƒν•΄μ•Όλ§Œ λ²„νŠΌμ΄ ν™œμ„±ν™”λ©λ‹ˆλ‹€. +

+
+ { + onUpdateProperty("componentConfig.action.requireRowSelection", checked); + }} + /> +
+ + {component.componentConfig?.action?.requireRowSelection && ( +
+
+ + +

+ μžλ™ 감지: ν…Œμ΄λΈ”, λΆ„ν•  νŒ¨λ„, ν”Œλ‘œμš° μœ„μ ― 쀑 μ„ νƒλœ ν•­λͺ©μ΄ 있으면 ν™œμ„±ν™” +

+
+ +
+
+ +

+ μ—¬λŸ¬ 행이 μ„ νƒλ˜μ–΄λ„ ν™œμ„±ν™” (κΈ°λ³Έ: 1개 이상 선택 μ‹œ ν™œμ„±ν™”) +

+
+ { + onUpdateProperty("componentConfig.action.allowMultiRowSelection", checked); + }} + /> +
+ + {!(component.componentConfig?.action?.allowMultiRowSelection ?? true) && ( +
+

+ μ •ν™•νžˆ 1개의 ν–‰λ§Œ μ„ νƒλ˜μ–΄μ•Ό λ²„νŠΌμ΄ ν™œμ„±ν™”λ©λ‹ˆλ‹€. +

+
+ )} +
+ )} +
+ {/* μ œμ–΄ κΈ°λŠ₯ μ„Ήμ…˜ */}
diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index ade1c5cc..8530d8e1 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -296,6 +296,84 @@ export const ButtonPrimaryComponent: React.FC = ({ return false; }, [component.componentConfig?.action, formData, vehicleStatus, statusLoading, component.label]); + // πŸ†• ν–‰ 선택 기반 λΉ„ν™œμ„±ν™” 쑰건 계산 + const isRowSelectionDisabled = useMemo(() => { + const actionConfig = component.componentConfig?.action; + + // requireRowSelection이 ν™œμ„±ν™”λ˜μ–΄ μžˆμ§€ μ•ŠμœΌλ©΄ λΉ„ν™œμ„±ν™”ν•˜μ§€ μ•ŠμŒ + if (!actionConfig?.requireRowSelection) { + return false; + } + + const rowSelectionSource = actionConfig.rowSelectionSource || "auto"; + const allowMultiRowSelection = actionConfig.allowMultiRowSelection ?? true; + + // μ„ νƒλœ 데이터 확인 + let hasSelection = false; + let selectionCount = 0; + + // 1. μžλ™ 감지 λͺ¨λ“œ λ˜λŠ” νŠΉμ • μ†ŒμŠ€ λͺ¨λ“œ + if (rowSelectionSource === "auto" || rowSelectionSource === "tableList") { + // TableListμ—μ„œ μ„ νƒλœ ν–‰ 확인 (props둜 전달됨) + if (selectedRowsData && selectedRowsData.length > 0) { + hasSelection = true; + selectionCount = selectedRowsData.length; + } + // λ˜λŠ” selectedRows prop 확인 + else if (selectedRows && selectedRows.length > 0) { + hasSelection = true; + selectionCount = selectedRows.length; + } + } + + if (rowSelectionSource === "auto" || rowSelectionSource === "splitPanelLeft") { + // λΆ„ν•  νŒ¨λ„ 쒌츑 선택 데이터 확인 + if (!hasSelection && splitPanelContext?.selectedLeftData) { + hasSelection = true; + selectionCount = 1; + } + } + + if (rowSelectionSource === "auto" || rowSelectionSource === "flowWidget") { + // ν”Œλ‘œμš° μœ„μ ― 선택 데이터 확인 + if (!hasSelection && flowSelectedData && flowSelectedData.length > 0) { + hasSelection = true; + selectionCount = flowSelectedData.length; + } + } + + // μ„ νƒλœ 데이터가 μ—†μœΌλ©΄ λΉ„ν™œμ„±ν™” + if (!hasSelection) { + console.log("🚫 [ButtonPrimary] ν–‰ 선택 ν•„μš” β†’ λΉ„ν™œμ„±ν™”:", component.label, { + rowSelectionSource, + hasSelection, + }); + return true; + } + + // 닀쀑 선택 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” 경우, μ •ν™•νžˆ 1개만 μ„ νƒλ˜μ–΄μ•Ό 함 + if (!allowMultiRowSelection && selectionCount !== 1) { + console.log("🚫 [ButtonPrimary] μ •ν™•νžˆ 1개 ν–‰ 선택 ν•„μš” β†’ λΉ„ν™œμ„±ν™”:", component.label, { + selectionCount, + allowMultiRowSelection, + }); + return true; + } + + console.log("βœ… [ButtonPrimary] ν–‰ 선택 쑰건 μΆ©μ‘±:", component.label, { + selectionCount, + rowSelectionSource, + }); + return false; + }, [ + component.componentConfig?.action, + component.label, + selectedRows, + selectedRowsData, + splitPanelContext?.selectedLeftData, + flowSelectedData, + ]); + // 확인 λ‹€μ΄μ–Όλ‘œκ·Έ μƒνƒœ const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [pendingAction, setPendingAction] = useState<{ @@ -1096,17 +1174,26 @@ export const ButtonPrimaryComponent: React.FC = ({ } } - // πŸ†• μ΅œμ’… λΉ„ν™œμ„±ν™” μƒνƒœ (μ„€μ • + 쑰건뢀 λΉ„ν™œμ„±ν™”) - const finalDisabled = componentConfig.disabled || isOperationButtonDisabled || statusLoading; + // πŸ†• μ΅œμ’… λΉ„ν™œμ„±ν™” μƒνƒœ (μ„€μ • + 쑰건뢀 λΉ„ν™œμ„±ν™” + ν–‰ 선택 ν•„μˆ˜) + const finalDisabled = componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading; // 곡톡 λ²„νŠΌ μŠ€νƒ€μΌ + // πŸ”§ component.styleμ—μ„œ background/backgroundColor 좩돌 λ°©μ§€ + const userStyle = component.style + ? Object.fromEntries( + Object.entries(component.style).filter( + ([key]) => !["width", "height", "background", "backgroundColor"].includes(key) + ) + ) + : {}; + const buttonElementStyle: React.CSSProperties = { width: "100%", height: "100%", minHeight: "40px", border: "none", borderRadius: "0.5rem", - background: finalDisabled ? "#e5e7eb" : buttonColor, + backgroundColor: finalDisabled ? "#e5e7eb" : buttonColor, // πŸ”§ background β†’ backgroundColor둜 λ³€κ²½ color: finalDisabled ? "#9ca3af" : "white", // πŸ”§ 크기 μ„€μ • 적용 (sm/md/lg) fontSize: componentConfig.size === "sm" ? "0.75rem" : componentConfig.size === "lg" ? "1rem" : "0.875rem", @@ -1122,10 +1209,8 @@ export const ButtonPrimaryComponent: React.FC = ({ margin: "0", lineHeight: "1.25", boxShadow: finalDisabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - // λ””μžμΈ λͺ¨λ“œμ™€ μΈν„°λž™ν‹°λΈŒ λͺ¨λ“œ λͺ¨λ‘μ—μ„œ μ‚¬μš©μž μŠ€νƒ€μΌ 적용 (width/height μ œμ™Έ) - ...(component.style - ? Object.fromEntries(Object.entries(component.style).filter(([key]) => key !== "width" && key !== "height")) - : {}), + // λ””μžμΈ λͺ¨λ“œμ™€ μΈν„°λž™ν‹°λΈŒ λͺ¨λ“œ λͺ¨λ‘μ—μ„œ μ‚¬μš©μž μŠ€νƒ€μΌ 적용 (width/height/background μ œμ™Έ) + ...userStyle, }; const buttonContent = processedConfig.text !== undefined ? processedConfig.text : component.label || "λ²„νŠΌ";