feat: add nested panel selection support in dynamic components
- Introduced a new callback `onNestedPanelSelect` to handle selections of components within nested split panels. - Updated the `RealtimePreviewDynamic`, `DynamicComponentRenderer`, and `TabsDesignEditor` components to support the new nested selection functionality. - Enhanced the layout management logic in `ScreenDesigner` to accommodate updates for nested structures, improving the overall user experience when interacting with nested components. Made-with: Cursor
This commit is contained in:
@@ -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<RealtimePreviewProps> = ({
|
||||
selectedTabComponentId, // 🆕 선택된 탭 컴포넌트 ID
|
||||
onSelectPanelComponent, // 🆕 분할 패널 내부 컴포넌트 선택 콜백
|
||||
selectedPanelComponentId, // 🆕 선택된 분할 패널 컴포넌트 ID
|
||||
onNestedPanelSelect,
|
||||
onResize, // 🆕 리사이즈 콜백
|
||||
}) => {
|
||||
// 🆕 화면 다국어 컨텍스트
|
||||
@@ -768,6 +770,7 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
||||
selectedTabComponentId={selectedTabComponentId}
|
||||
onSelectPanelComponent={onSelectPanelComponent}
|
||||
selectedPanelComponentId={selectedPanelComponentId}
|
||||
onNestedPanelSelect={onNestedPanelSelect}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user