diff --git a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx index 5857e88e..923c1aa3 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx @@ -221,6 +221,8 @@ export const SplitPanelLayoutComponent: React.FC const [resizeSize, setResizeSize] = useState<{ width: number; height: number } | null>(null); // πŸ†• μ™ΈλΆ€μ—μ„œ 전달받은 선택 μƒνƒœ μ‚¬μš© (νƒ­ μ»΄ν¬λ„ŒνŠΈμ™€ 동일 ꡬ쑰) const selectedPanelComponentId = externalSelectedPanelComponentId || null; + // πŸ†• μ»€μŠ€ν…€ λͺ¨λ“œ: λΆ„ν• νŒ¨λ„ λ‚΄ νƒ­ μ»΄ν¬λ„ŒνŠΈμ˜ 선택 μƒνƒœ 관리 + const [nestedTabSelectedCompId, setNestedTabSelectedCompId] = useState(undefined); const rafRef = useRef(null); // πŸ†• 10px λ‹¨μœ„ μŠ€λƒ… ν•¨μˆ˜ @@ -300,8 +302,9 @@ export const SplitPanelLayoutComponent: React.FC rafRef.current = requestAnimationFrame(() => { const deltaX = moveEvent.clientX - startMouseX; const deltaY = moveEvent.clientY - startMouseY; - const newX = Math.max(0, startLeft + deltaX); - const newY = Math.max(0, startTop + deltaY); + // 10px λ‹¨μœ„ μŠ€λƒ… 적용 + const newX = snapTo10(Math.max(0, startLeft + deltaX)); + const newY = snapTo10(Math.max(0, startTop + deltaY)); setDragPosition({ x: newX, y: newY }); }); }; @@ -317,8 +320,9 @@ export const SplitPanelLayoutComponent: React.FC const deltaX = upEvent.clientX - startMouseX; const deltaY = upEvent.clientY - startMouseY; - const newX = Math.max(0, startLeft + deltaX); - const newY = Math.max(0, startTop + deltaY); + // 10px λ‹¨μœ„ μŠ€λƒ… 적용 + const newX = snapTo10(Math.max(0, startLeft + deltaX)); + const newY = snapTo10(Math.max(0, startTop + deltaY)); setDraggingCompId(null); setDragPosition(null); @@ -328,7 +332,7 @@ export const SplitPanelLayoutComponent: React.FC const panelConfig = componentConfig[panelKey] || {}; const updatedComponents = (panelConfig.components || []).map((c: PanelInlineComponent) => c.id === comp.id - ? { ...c, position: { x: Math.round(newX), y: Math.round(newY) } } + ? { ...c, position: { x: newX, y: newY } } : c ); @@ -348,7 +352,7 @@ export const SplitPanelLayoutComponent: React.FC document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }, - [component, componentConfig, onUpdateComponent] + [component, componentConfig, onUpdateComponent, snapTo10] ); // πŸ†• μ»€μŠ€ν…€ λͺ¨λ“œ: λ¦¬μ‚¬μ΄μ¦ˆ μ‹œμž‘ ν•Έλ“€λŸ¬ @@ -2601,6 +2605,10 @@ export const SplitPanelLayoutComponent: React.FC }} onClick={(e) => { e.stopPropagation(); + // νŒ¨λ„ μ»΄ν¬λ„ŒνŠΈ 선택 μ‹œ νƒ­ λ‚΄ 선택 ν•΄μ œ + if (comp.componentType !== "v2-tabs-widget") { + setNestedTabSelectedCompId(undefined); + } onSelectPanelComponent?.("left", comp.id, comp); }} > @@ -2680,6 +2688,8 @@ export const SplitPanelLayoutComponent: React.FC // πŸ†• μ€‘μ²©λœ νƒ­ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 ν•Έλ“€λŸ¬ - λΆ€λͺ¨ λΆ„ν•  νŒ¨λ„ 정보 포함 onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => { console.log("πŸ” [SplitPanel-Left] onSelectTabComponent 호좜:", { tabId, compId, tabComp, parentSplitPanelId: component.id }); + // νƒ­ λ‚΄ μ»΄ν¬λ„ŒνŠΈ 선택 μƒνƒœ μ—…λ°μ΄νŠΈ + setNestedTabSelectedCompId(compId); // λΆ€λͺ¨ λΆ„ν•  νŒ¨λ„ 정보와 ν•¨κ»˜ μ „μ—­ 이벀트 λ°œμƒ const event = new CustomEvent("nested-tab-component-select", { detail: { @@ -2693,7 +2703,7 @@ export const SplitPanelLayoutComponent: React.FC }); window.dispatchEvent(event); }} - selectedTabComponentId={undefined} + selectedTabComponentId={nestedTabSelectedCompId} /> @@ -3494,6 +3504,10 @@ export const SplitPanelLayoutComponent: React.FC }} onClick={(e) => { e.stopPropagation(); + // νŒ¨λ„ μ»΄ν¬λ„ŒνŠΈ 선택 μ‹œ νƒ­ λ‚΄ 선택 ν•΄μ œ + if (comp.componentType !== "v2-tabs-widget") { + setNestedTabSelectedCompId(undefined); + } onSelectPanelComponent?.("right", comp.id, comp); }} > @@ -3573,6 +3587,8 @@ export const SplitPanelLayoutComponent: React.FC // πŸ†• μ€‘μ²©λœ νƒ­ λ‚΄λΆ€ μ»΄ν¬λ„ŒνŠΈ 선택 ν•Έλ“€λŸ¬ - λΆ€λͺ¨ λΆ„ν•  νŒ¨λ„ 정보 포함 onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => { console.log("πŸ” [SplitPanel-Right] onSelectTabComponent 호좜:", { tabId, compId, tabComp, parentSplitPanelId: component.id }); + // νƒ­ λ‚΄ μ»΄ν¬λ„ŒνŠΈ 선택 μƒνƒœ μ—…λ°μ΄νŠΈ + setNestedTabSelectedCompId(compId); // λΆ€λͺ¨ λΆ„ν•  νŒ¨λ„ 정보와 ν•¨κ»˜ μ „μ—­ 이벀트 λ°œμƒ const event = new CustomEvent("nested-tab-component-select", { detail: { @@ -3586,7 +3602,7 @@ export const SplitPanelLayoutComponent: React.FC }); window.dispatchEvent(event); }} - selectedTabComponentId={undefined} + selectedTabComponentId={nestedTabSelectedCompId} /> diff --git a/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx b/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx index 8f14a250..becd3c34 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx @@ -186,9 +186,10 @@ export const TableListConfigPanel: React.FC = ({ return; } - // πŸ†• customTableName이 μ„€μ •λœ 경우 λ°˜λ“œμ‹œ APIμ—μ„œ κ°€μ Έμ˜€κΈ° - // tableColumns prop은 ν™”λ©΄μ˜ κΈ°λ³Έ ν…Œμ΄λΈ” μ»¬λŸΌμ΄λ―€λ‘œ, customTableName μ‚¬μš© μ‹œ λ¬΄μ‹œ - const shouldUseTableColumnsProp = !config.useCustomTable && tableColumns && tableColumns.length > 0; + // tableColumns prop은 ν™”λ©΄μ˜ κΈ°λ³Έ ν…Œμ΄λΈ” μ»¬λŸΌμ΄λ―€λ‘œ, + // λ‹€λ₯Έ ν…Œμ΄λΈ”μ„ μ„ νƒν•œ 경우 λ°˜λ“œμ‹œ APIμ—μ„œ κ°€μ Έμ˜€κΈ° + const isUsingDifferentTable = config.selectedTable && screenTableName && config.selectedTable !== screenTableName; + const shouldUseTableColumnsProp = !config.useCustomTable && !isUsingDifferentTable && tableColumns && tableColumns.length > 0; if (shouldUseTableColumnsProp) { const mappedColumns = tableColumns.map((column: any) => ({ @@ -772,11 +773,113 @@ export const TableListConfigPanel: React.FC = ({ handleChange("columns", columns); }; + // ν…Œμ΄λΈ” λ³€κ²½ ν•Έλ“€λŸ¬ - ν…Œμ΄λΈ” λ³€κ²½ μ‹œ 컬럼 μ„€μ • μ΄ˆκΈ°ν™” + const handleTableChange = (newTableName: string) => { + if (newTableName === targetTableName) return; + + const updatedConfig = { + ...config, + selectedTable: newTableName, + // ν…Œμ΄λΈ”μ΄ λ³€κ²½λ˜λ©΄ 컬럼 μ„€μ • μ΄ˆκΈ°ν™” + columns: [], + }; + onChange(updatedConfig); + setTableComboboxOpen(false); + }; + return (
ν…Œμ΄λΈ” 리슀트 μ„€μ •
+ {/* ν…Œμ΄λΈ” 선택 */} +
+
+

데이터 μ†ŒμŠ€

+

+ ν…Œμ΄λΈ”μ„ μ„ νƒν•˜μ„Έμš”. 미선택 μ‹œ ν™”λ©΄ 메인 ν…Œμ΄λΈ”μ„ μ‚¬μš©ν•©λ‹ˆλ‹€. +

+
+
+
+ + + + + + + + + + ν…Œμ΄λΈ”μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. + + {availableTables.map((table) => ( + handleTableChange(table.tableName)} + className="text-xs" + > + +
+ {table.displayName} + {table.displayName !== table.tableName && ( + {table.tableName} + )} +
+
+ ))} +
+
+
+
+
+ {screenTableName && targetTableName !== screenTableName && ( +
+ + ν™”λ©΄ κΈ°λ³Έ ν…Œμ΄λΈ”({screenTableName})κ³Ό λ‹€λ₯Έ ν…Œμ΄λΈ”μ„ μ‚¬μš© 쀑 + + +
+ )} +
+
+ {/* νˆ΄λ°” λ²„νŠΌ μ„€μ • */}
@@ -1167,11 +1270,11 @@ export const TableListConfigPanel: React.FC = ({
)} - {!screenTableName ? ( + {!targetTableName ? (
-

ν…Œμ΄λΈ”μ΄ μ—°κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

-

화면에 ν…Œμ΄λΈ”μ„ μ—°κ²°ν•œ ν›„ μ»¬λŸΌμ„ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

+

ν…Œμ΄λΈ”μ΄ μ„ νƒλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

+

μœ„ 데이터 μ†ŒμŠ€μ—μ„œ ν…Œμ΄λΈ”μ„ μ„ νƒν•˜μ„Έμš”.

) : availableColumns.length === 0 ? ( 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 8645fed9..0a04f76d 100644 --- a/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx +++ b/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx @@ -78,6 +78,9 @@ const TabsDesignEditor: React.FC<{ [activeTabId, component, onUpdateComponent, tabs] ); + // 10px λ‹¨μœ„ μŠ€λƒ… ν•¨μˆ˜ + const snapTo10 = useCallback((value: number) => Math.round(value / 10) * 10, []); + // μ»΄ν¬λ„ŒνŠΈ λ“œλž˜κ·Έ μ‹œμž‘ const handleDragStart = useCallback( (e: React.MouseEvent, comp: TabInlineComponent) => { @@ -104,9 +107,9 @@ const TabsDesignEditor: React.FC<{ const deltaX = moveEvent.clientX - startMouseX; const deltaY = moveEvent.clientY - startMouseY; - // μƒˆ μœ„μΉ˜ = μ‹œμž‘ μœ„μΉ˜ + μ΄λ™λŸ‰ - const newX = Math.max(0, startLeft + deltaX); - const newY = Math.max(0, startTop + deltaY); + // μƒˆ μœ„μΉ˜ = μ‹œμž‘ μœ„μΉ˜ + μ΄λ™λŸ‰ (10px λ‹¨μœ„ μŠ€λƒ… 적용) + const newX = snapTo10(Math.max(0, startLeft + deltaX)); + const newY = snapTo10(Math.max(0, startTop + deltaY)); // React μƒνƒœλ‘œ μœ„μΉ˜ μ—…λ°μ΄νŠΈ (λ¦¬λ Œλ”λ§ 트리거) setDragPosition({ x: newX, y: newY }); @@ -126,9 +129,9 @@ const TabsDesignEditor: React.FC<{ const deltaX = upEvent.clientX - startMouseX; const deltaY = upEvent.clientY - startMouseY; - // μƒˆ μœ„μΉ˜ = μ‹œμž‘ μœ„μΉ˜ + μ΄λ™λŸ‰ - const newX = Math.max(0, startLeft + deltaX); - const newY = Math.max(0, startTop + deltaY); + // μƒˆ μœ„μΉ˜ = μ‹œμž‘ μœ„μΉ˜ + μ΄λ™λŸ‰ (10px λ‹¨μœ„ μŠ€λƒ… 적용) + const newX = snapTo10(Math.max(0, startLeft + deltaX)); + const newY = snapTo10(Math.max(0, startTop + deltaY)); setDraggingCompId(null); setDragPosition(null); @@ -144,8 +147,8 @@ const TabsDesignEditor: React.FC<{ ? { ...c, position: { - x: Math.max(0, Math.round(newX)), - y: Math.max(0, Math.round(newY)), + x: newX, + y: newY, }, } : c @@ -172,12 +175,9 @@ const TabsDesignEditor: React.FC<{ document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }, - [activeTabId, component, onUpdateComponent, tabs] + [activeTabId, component, onUpdateComponent, tabs, snapTo10] ); - // 10px λ‹¨μœ„ μŠ€λƒ… ν•¨μˆ˜ - const snapTo10 = useCallback((value: number) => Math.round(value / 10) * 10, []); - // λ¦¬μ‚¬μ΄μ¦ˆ μ‹œμž‘ ν•Έλ“€λŸ¬ const handleResizeStart = useCallback( (e: React.MouseEvent, comp: TabInlineComponent, direction: "e" | "s" | "se") => {