diff --git a/src/App.jsx b/src/App.jsx index 15bdf41..5a6803d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1350,6 +1350,114 @@ function RelationPopup({ programs, onToggleRelation, onClose }) { ); } +function RelationTreePanel({ programs, onProgramClick, onOpenRelationPopup }) { + const [expandedProgramIds, setExpandedProgramIds] = useState(() => new Set(programs.map((program) => program.id))); + const programMap = Object.fromEntries(programs.map((program) => [program.id, program])); + const linkedChildIds = new Set(programs.flatMap((program) => program.successors ?? [])); + const rootPrograms = programs.filter((program) => !linkedChildIds.has(program.id)); + const visibleRoots = rootPrograms.length ? rootPrograms : programs.slice(0, 1); + + const toggleExpanded = (programId) => { + setExpandedProgramIds((current) => { + const next = new Set(current); + if (next.has(programId)) { + next.delete(programId); + } else { + next.add(programId); + } + return next; + }); + }; + + const renderNode = (program, depth = 0, visitedIds = new Set()) => { + const successors = (program.successors ?? []) + .map((successorId) => programMap[successorId]) + .filter(Boolean); + const hasChildren = successors.length > 0; + const isExpanded = expandedProgramIds.has(program.id); + const isRepeated = visitedIds.has(program.id); + const hasMultipleInputs = (program.predecessors ?? []).length > 1; + const nextVisitedIds = new Set(visitedIds); + nextVisitedIds.add(program.id); + + return ( +
+ {depth > 0 && ( +
+ ); + }; + + return ( + + ); +} + export default function App() { const urlParams = new URLSearchParams(window.location.search); const isDetailWindow = urlParams.get('view') === 'program-detail' || urlParams.get('view') === 'cheonjiin-detail'; @@ -1996,15 +2104,6 @@ export default function App() {
-
+
+ setIsRelationPopupOpen(true)} + /> + +
{isEditing && ( -
+

프로그램 편집 @@ -2037,7 +2144,7 @@ export default function App() { 프로그램 추가 -

+
)} ))} + +
+ {isRelationPopupOpen && (