diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index 17615147..9c19405c 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -47,6 +47,7 @@ interface RealtimePreviewProps { selectedTabComponentId?: string; // πŸ†• μ„ νƒλœ νƒ­ μ»΄ν¬λ„ŒνŠΈ ID onSelectPanelComponent?: (panelSide: "left" | "right", compId: string, comp: any) => void; // πŸ†• λΆ„ν•  νŒ¨λ„ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 콜백 selectedPanelComponentId?: string; // πŸ†• μ„ νƒλœ λΆ„ν•  νŒ¨λ„ μ»΄ν¬λ„ŒνŠΈ ID + onNestedPanelSelect?: (splitPanelId: string, panelSide: "left" | "right", compId: string, comp: any) => void; onResize?: (componentId: string, newSize: { width: number; height: number }) => void; // πŸ†• λ¦¬μ‚¬μ΄μ¦ˆ 콜백 // λ²„νŠΌ μ•‘μ…˜μ„ μœ„ν•œ props @@ -150,6 +151,7 @@ const RealtimePreviewDynamicComponent: React.FC = ({ selectedTabComponentId, // πŸ†• μ„ νƒλœ νƒ­ μ»΄ν¬λ„ŒνŠΈ ID onSelectPanelComponent, // πŸ†• λΆ„ν•  νŒ¨λ„ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 콜백 selectedPanelComponentId, // πŸ†• μ„ νƒλœ λΆ„ν•  νŒ¨λ„ μ»΄ν¬λ„ŒνŠΈ ID + onNestedPanelSelect, onResize, // πŸ†• λ¦¬μ‚¬μ΄μ¦ˆ 콜백 }) => { // πŸ†• ν™”λ©΄ λ‹€κ΅­μ–΄ μ»¨ν…μŠ€νŠΈ @@ -768,6 +770,7 @@ const RealtimePreviewDynamicComponent: React.FC = ({ selectedTabComponentId={selectedTabComponentId} onSelectPanelComponent={onSelectPanelComponent} selectedPanelComponentId={selectedPanelComponentId} + onNestedPanelSelect={onNestedPanelSelect} /> diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index e19f01f1..6eeeb4e1 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -6744,15 +6744,6 @@ export default function ScreenDesigner({ const { splitPanelId, panelSide } = selectedPanelComponentInfo; const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel"; - console.log("πŸ”§ updatePanelComponentProperty 호좜:", { - componentId, - path, - value, - splitPanelId, - panelSide, - }); - - // πŸ†• μ•ˆμ „ν•œ κΉŠμ€ 경둜 μ—…λ°μ΄νŠΈ 헬퍼 ν•¨μˆ˜ const setNestedValue = (obj: any, pathStr: string, val: any): any => { const result = JSON.parse(JSON.stringify(obj)); const parts = pathStr.split("."); @@ -6769,9 +6760,27 @@ export default function ScreenDesigner({ return result; }; + // 쀑첩 ꡬ쑰 포함 λΆ„ν• νŒ¨λ„ μ°ΎκΈ° 헬퍼 + const findSplitPanelInLayout = (components: any[]): { found: any; path: "top" | "nested"; parentTabId?: string; parentTabTabId?: string } | null => { + const direct = components.find((c) => c.id === splitPanelId); + if (direct) return { found: direct, path: "top" }; + for (const comp of components) { + const ct = (comp as any)?.componentType || (comp as any)?.overrides?.type; + const cfg = (comp as any)?.componentConfig || (comp as any)?.overrides || {}; + if (ct === "tabs-widget" || ct === "v2-tabs-widget") { + for (const tab of (cfg.tabs || [])) { + const nested = (tab.components || []).find((c: any) => c.id === splitPanelId); + if (nested) return { found: nested, path: "nested", parentTabId: comp.id, parentTabTabId: tab.id }; + } + } + } + return null; + }; + setLayout((prevLayout) => { - const splitPanelComponent = prevLayout.components.find((c) => c.id === splitPanelId); - if (!splitPanelComponent) return prevLayout; + const result = findSplitPanelInLayout(prevLayout.components); + if (!result) return prevLayout; + const splitPanelComponent = result.found; const currentConfig = (splitPanelComponent as any).componentConfig || {}; const panelConfig = currentConfig[panelKey] || {}; @@ -6807,17 +6816,37 @@ export default function ScreenDesigner({ }, }; - // selectedPanelComponentInfo μ—…λ°μ΄νŠΈ setSelectedPanelComponentInfo((prev) => prev ? { ...prev, component: updatedComp } : null, ); - return { - ...prevLayout, - components: prevLayout.components.map((c) => - c.id === splitPanelId ? updatedComponent : c, - ), + // 쀑첩 ꡬ쑰 반영 + const applyUpdatedSplitPanel = (layout: any, updated: any, info: any) => { + if (info.path === "top") { + return { ...layout, components: layout.components.map((c: any) => c.id === splitPanelId ? updated : c) }; + } + return { + ...layout, + components: layout.components.map((c: any) => { + if (c.id !== info.parentTabId) return c; + const cfgKey = c.componentConfig?.tabs ? "componentConfig" : "overrides"; + const cfg = c[cfgKey] || {}; + return { + ...c, + [cfgKey]: { + ...cfg, + tabs: (cfg.tabs || []).map((t: any) => + t.id === info.parentTabTabId + ? { ...t, components: (t.components || []).map((tc: any) => tc.id === splitPanelId ? updated : tc) } + : t, + ), + }, + }; + }), + }; }; + + return applyUpdatedSplitPanel(prevLayout, updatedComponent, result); }); }; @@ -6827,8 +6856,23 @@ export default function ScreenDesigner({ const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel"; setLayout((prevLayout) => { - const splitPanelComponent = prevLayout.components.find((c) => c.id === splitPanelId); - if (!splitPanelComponent) return prevLayout; + const findResult = (() => { + const direct = prevLayout.components.find((c: any) => c.id === splitPanelId); + if (direct) return { found: direct, path: "top" as const }; + for (const comp of prevLayout.components) { + const ct = (comp as any)?.componentType || (comp as any)?.overrides?.type; + const cfg = (comp as any)?.componentConfig || (comp as any)?.overrides || {}; + if (ct === "tabs-widget" || ct === "v2-tabs-widget") { + for (const tab of (cfg.tabs || [])) { + const nested = (tab.components || []).find((c: any) => c.id === splitPanelId); + if (nested) return { found: nested, path: "nested" as const, parentTabId: comp.id, parentTabTabId: tab.id }; + } + } + } + return null; + })(); + if (!findResult) return prevLayout; + const splitPanelComponent = findResult.found; const currentConfig = (splitPanelComponent as any).componentConfig || {}; const panelConfig = currentConfig[panelKey] || {}; @@ -6849,11 +6893,27 @@ export default function ScreenDesigner({ setSelectedPanelComponentInfo(null); + if (findResult.path === "top") { + return { ...prevLayout, components: prevLayout.components.map((c: any) => c.id === splitPanelId ? updatedComponent : c) }; + } return { ...prevLayout, - components: prevLayout.components.map((c) => - c.id === splitPanelId ? updatedComponent : c, - ), + components: prevLayout.components.map((c: any) => { + if (c.id !== findResult.parentTabId) return c; + const cfgKey = c.componentConfig?.tabs ? "componentConfig" : "overrides"; + const cfg = c[cfgKey] || {}; + return { + ...c, + [cfgKey]: { + ...cfg, + tabs: (cfg.tabs || []).map((t: any) => + t.id === findResult.parentTabTabId + ? { ...t, components: (t.components || []).map((tc: any) => tc.id === splitPanelId ? updatedComponent : tc) } + : t, + ), + }, + }; + }), }; }); }; @@ -7457,6 +7517,7 @@ export default function ScreenDesigner({ onSelectPanelComponent={(panelSide, compId, comp) => handleSelectPanelComponent(component.id, panelSide, compId, comp) } + onNestedPanelSelect={handleSelectPanelComponent} selectedPanelComponentId={ selectedPanelComponentInfo?.splitPanelId === component.id ? selectedPanelComponentInfo.componentId diff --git a/frontend/lib/registry/DynamicComponentRenderer.tsx b/frontend/lib/registry/DynamicComponentRenderer.tsx index 8ee1ba20..867c8a86 100644 --- a/frontend/lib/registry/DynamicComponentRenderer.tsx +++ b/frontend/lib/registry/DynamicComponentRenderer.tsx @@ -235,6 +235,8 @@ export interface DynamicComponentRendererProps { // πŸ†• λΆ„ν•  νŒ¨λ„ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 콜백 onSelectPanelComponent?: (panelSide: "left" | "right", compId: string, comp: any) => void; selectedPanelComponentId?: string; + // μ€‘μ²©λœ λΆ„ν• νŒ¨λ„ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 콜백 (νƒ­ μ•ˆμ˜ λΆ„ν• νŒ¨λ„) + onNestedPanelSelect?: (splitPanelId: string, panelSide: "left" | "right", compId: string, comp: any) => void; flowSelectedStepId?: number | null; onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void; // ν…Œμ΄λΈ” μƒˆλ‘œκ³ μΉ¨ ν‚€ @@ -868,6 +870,7 @@ export const DynamicComponentRenderer: React.FC = // πŸ†• λΆ„ν•  νŒ¨λ„ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 콜백 onSelectPanelComponent: props.onSelectPanelComponent, selectedPanelComponentId: props.selectedPanelComponentId, + onNestedPanelSelect: props.onNestedPanelSelect, }; // λ Œλ”λŸ¬κ°€ ν΄λž˜μŠ€μΈμ§€ ν•¨μˆ˜μΈμ§€ 확인 diff --git a/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx b/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx index efd76407..ac6b208e 100644 --- a/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx +++ b/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx @@ -15,7 +15,8 @@ const TabsDesignEditor: React.FC<{ onUpdateComponent?: (updatedComponent: any) => void; onSelectTabComponent?: (tabId: string, compId: string, comp: TabInlineComponent) => void; selectedTabComponentId?: string; -}> = ({ component, tabs, onUpdateComponent, onSelectTabComponent, selectedTabComponentId }) => { + onNestedPanelSelect?: (splitPanelId: string, panelSide: "left" | "right", compId: string, comp: any) => void; +}> = ({ component, tabs, onUpdateComponent, onSelectTabComponent, selectedTabComponentId, onNestedPanelSelect }) => { const [activeTabId, setActiveTabId] = useState(tabs[0]?.id || ""); const [draggingCompId, setDraggingCompId] = useState(null); const [dragPosition, setDragPosition] = useState<{ x: number; y: number } | null>(null); @@ -443,7 +444,11 @@ const TabsDesignEditor: React.FC<{ }); }, onSelectPanelComponent: (panelSide: string, compId: string, panelComp: any) => { - onSelectTabComponent?.(activeTabId, comp.id, { ...comp, _selectedPanelSide: panelSide, _selectedPanelCompId: compId, _selectedPanelComp: panelComp } as any); + if (onNestedPanelSelect) { + onNestedPanelSelect(comp.id, panelSide as "left" | "right", compId, panelComp); + } else { + onSelectTabComponent?.(activeTabId, comp.id, comp); + } }, } : {})} /> @@ -506,6 +511,7 @@ const TabsWidgetWrapper: React.FC = (props) => { onUpdateComponent, onSelectTabComponent, selectedTabComponentId, + onNestedPanelSelect, ...restProps } = props; @@ -522,6 +528,7 @@ const TabsWidgetWrapper: React.FC = (props) => { onUpdateComponent={onUpdateComponent} onSelectTabComponent={onSelectTabComponent} selectedTabComponentId={selectedTabComponentId} + onNestedPanelSelect={onNestedPanelSelect} /> ); }