@@ -649,41 +649,60 @@ export function LearningPathMap() {
649
649
{ /* Sprechblase mit Button (bei Hervorhebung) */ }
650
650
{ activeBubble === i && (
651
651
< g onClick = { e => e . stopPropagation ( ) } >
652
- < polygon
653
- points = { `${ cx } ,${ cy + radius - 5 } ${ cx - 20 } ,${ cy + radius + 10.5 } ${ cx + 20 } ,${ cy + radius + 10.5 } ` }
654
- fill = "rgba(255,255,255,0.9)"
655
- className = "filter drop-shadow-md"
656
- />
657
- < foreignObject
658
- x = "10%"
659
- y = { cy + radius + 10 }
660
- width = "80%"
661
- height = { 120 }
662
- >
663
- < div className = "bg-white bg-opacity-90 p-2 rounded-3xl shadow-md text-center text-sm h-full z-100 flex flex-col items-center justify-around" >
664
- < p className = "font-bold text-lg" > { el . source . title } </ p >
665
- < button
666
- className = "bg-blue-500 text-white py-2 px-4 rounded-full hover:bg-blue-600 transition-colors"
667
- onClick = { e => {
668
- e . stopPropagation ( )
669
- setActiveBubble ( null )
670
- handleLearningPathStepClick ( getClickParams ( ) )
671
- } }
672
- >
673
- { el . solvedPercentage > 0
674
- ? el . source . type === 'challenge'
675
- ? 'Challenge weiter'
676
- : el . source . type === 'video'
677
- ? 'Video weiter'
678
- : 'Aufgabe weiter'
679
- : el . source . type === 'challenge'
680
- ? 'Challenge starten'
681
- : el . source . type === 'video'
682
- ? 'Video starten'
683
- : 'Aufgabe starten' }
684
- </ button >
685
- </ div >
686
- </ foreignObject >
652
+ { ( ( ) => {
653
+ const svgWidth = 375 // Breite des viewBox
654
+ const bubbleWidth = svgWidth * 0.8 // 80% der Breite (ca. 300px)
655
+ const margin = 15 // 15px Abstand zu beiden Rändern
656
+ const minX = margin // Minimaler x-Wert für die Bubble
657
+ const maxX = svgWidth - bubbleWidth - margin // Maximaler x-Wert
658
+ let offsetX = cx - bubbleWidth / 2 // Bubble zentriert zum Knoten
659
+
660
+ if ( offsetX < minX ) offsetX = minX
661
+ if ( offsetX > maxX ) offsetX = maxX
662
+
663
+ return (
664
+ < >
665
+ { /* Pfeil (Polygon) */ }
666
+ < polygon
667
+ points = { `${ cx } ,${ cy + radius - 5 } ${ cx - 20 } ,${ cy + radius + 10.5 } ${ cx + 20 } ,${ cy + radius + 10.5 } ` }
668
+ fill = "rgba(255,255,255,0.9)"
669
+ className = "filter drop-shadow-md"
670
+ />
671
+ < foreignObject
672
+ x = { offsetX }
673
+ y = { cy + radius + 10 }
674
+ width = "80%"
675
+ height = { 120 }
676
+ >
677
+ < div className = "bg-white bg-opacity-90 p-2 rounded-3xl shadow-md text-center text-sm h-full z-100 flex flex-col items-center justify-around" >
678
+ < p className = "font-bold text-lg" >
679
+ { el . source . title }
680
+ </ p >
681
+ < button
682
+ className = "bg-blue-500 text-white py-2 px-4 rounded-full hover:bg-blue-600 transition-colors"
683
+ onClick = { e => {
684
+ e . stopPropagation ( )
685
+ setActiveBubble ( null )
686
+ handleLearningPathStepClick ( getClickParams ( ) )
687
+ } }
688
+ >
689
+ { el . solvedPercentage > 0
690
+ ? el . source . type === 'challenge'
691
+ ? 'Challenge weiter'
692
+ : el . source . type === 'video'
693
+ ? 'Video weiter'
694
+ : 'Aufgabe weiter'
695
+ : el . source . type === 'challenge'
696
+ ? 'Challenge starten'
697
+ : el . source . type === 'video'
698
+ ? 'Video starten'
699
+ : 'Aufgabe starten' }
700
+ </ button >
701
+ </ div >
702
+ </ foreignObject > { ' ' }
703
+ </ >
704
+ )
705
+ } ) ( ) }
687
706
</ g >
688
707
) }
689
708
0 commit comments