@@ -35,6 +35,7 @@ import {
3535 parseEntityIdPath ,
3636 appendAttributeToPath ,
3737 extractEntityIds ,
38+ extractEntityPath ,
3839 buildAttributeLookupKey ,
3940} from '../../../utils/entityIdPath' ;
4041import {
@@ -144,6 +145,9 @@ const MappingsView: React.FC = () => {
144145 const [ selectedTargetAttrId , setSelectedTargetAttrId ] = useState <
145146 number | null
146147 > ( null ) ;
148+ const [ selectedTargetAttrPath , setSelectedTargetAttrPath ] = useState <
149+ string | null
150+ > ( null ) ;
147151 const [ selectionIndex , setSelectionIndex ] = useState ( 0 ) ;
148152 const [ selectionAll , setSelectionAll ] = useState ( false ) ;
149153 const [ selectedTransformationIds , setSelectedTransformationIds ] =
@@ -158,6 +162,9 @@ const MappingsView: React.FC = () => {
158162 const [ reassignHoverTargetId , setReassignHoverTargetId ] = useState <
159163 number | null
160164 > ( null ) ;
165+ const [ reassignHoverTargetPath , setReassignHoverTargetPath ] = useState <
166+ string | null
167+ > ( null ) ;
161168 // Multi-wire (per-transformation) selection: store source AttributeIds for the currently selected transformation
162169 const [ selectedWireSourceAttrIds , setSelectedWireSourceAttrIds ] = useState < Set < number > > ( new Set ( ) ) ;
163170 // Multi-wire detach drag state (supports one or many selected source wires for the single selected transformation)
@@ -173,6 +180,7 @@ const MappingsView: React.FC = () => {
173180 // the latest value without needing reassignHoverTargetId in the dep array
174181 // (which caused effect re-runs + duplicate handleUp registrations).
175182 const reassignHoverTargetIdRef = useRef < number | null > ( null ) ;
183+ const reassignHoverTargetPathRef = useRef < string | null > ( null ) ;
176184 // Guard against duplicate handleUp invocations caused by the synthetic mouseup
177185 // that handleMove dispatches after the button is released.
178186 const reassignProcessingRef = useRef ( false ) ;
@@ -249,8 +257,11 @@ const MappingsView: React.FC = () => {
249257 setReassignTransformations ( [ ] ) ;
250258 setReassignPaths ( [ ] ) ;
251259 setReassignHoverTargetId ( null ) ;
260+ setReassignHoverTargetPath ( null ) ;
252261 reassignHoverTargetIdRef . current = null ;
262+ reassignHoverTargetPathRef . current = null ;
253263 setSelectedTargetAttrId ( null ) ;
264+ setSelectedTargetAttrPath ( null ) ;
254265 setSelectedTransformationIds ( new Set ( ) ) ;
255266 setSelectionAll ( false ) ;
256267 } , [ ] ) ;
@@ -1552,19 +1563,23 @@ const MappingsView: React.FC = () => {
15521563 ) as HTMLElement | null ;
15531564 let node : HTMLElement | null = el ;
15541565 let targetId : number | null = null ;
1566+ let targetEntityPath : string | null = null ;
15551567 while ( node ) {
15561568 if (
15571569 node . classList ?. contains ( 'mappings-attr' ) &&
15581570 node . dataset . attrId
15591571 ) {
15601572 if ( node . classList . contains ( 'mappings-attr--right' ) )
15611573 targetId = Number ( node . dataset . attrId ) ;
1574+ targetEntityPath = node . dataset . entityPath || null ;
15621575 break ;
15631576 }
15641577 node = node . parentElement ;
15651578 }
15661579 reassignHoverTargetIdRef . current = targetId ;
1580+ reassignHoverTargetPathRef . current = targetEntityPath ;
15671581 setReassignHoverTargetId ( targetId ) ;
1582+ setReassignHoverTargetPath ( targetEntityPath ) ;
15681583 } ;
15691584 const handleUp = async ( ) => {
15701585 // Guard: prevent duplicate invocations caused by the synthetic mouseup
@@ -1574,17 +1589,35 @@ const MappingsView: React.FC = () => {
15741589 // Read from ref so we always get the latest hover target, regardless of
15751590 // which effect-closure version of handleUp fires.
15761591 const reassignHoverTargetId = reassignHoverTargetIdRef . current ;
1577- const droppingOnTarget =
1578- ! ! reassignHoverTargetId &&
1579- reassignHoverTargetId !== selectedTargetAttrId ;
1592+ const reassignHoverTargetEntityPath = reassignHoverTargetPathRef . current ;
1593+ // Determine if drop target is genuinely different from the current target.
1594+ // Compare both attribute ID and entity path so that the same attribute in a
1595+ // different entity is recognised as a valid drop target.
1596+ const droppingOnTarget = ( ( ) => {
1597+ if ( ! reassignHoverTargetId ) return false ;
1598+ const currentT = reassignTransformations [ 0 ] ;
1599+ const currentTgtAttrId = currentT ?. TargetAttribute ?. AttributeId ;
1600+ const currentTgtEntityIdPath = ( currentT ?. TargetAttribute as any ) ?. EntityIdPath ;
1601+ const currentTgtEntityPath = currentTgtEntityIdPath
1602+ ? extractEntityPath ( currentTgtEntityIdPath )
1603+ : null ;
1604+ // Different attribute ID → definitely a different target
1605+ if ( reassignHoverTargetId !== currentTgtAttrId ) return true ;
1606+ // Same attribute ID but different entity path → different target
1607+ if (
1608+ reassignHoverTargetEntityPath != null &&
1609+ currentTgtEntityPath != null &&
1610+ reassignHoverTargetEntityPath !== currentTgtEntityPath
1611+ ) return true ;
1612+ // One path known and the other not → treat as different
1613+ if (
1614+ ( reassignHoverTargetEntityPath != null ) !== ( currentTgtEntityPath != null )
1615+ ) return true ;
1616+ return false ;
1617+ } ) ( ) ;
15801618 try {
15811619 if ( droppingOnTarget ) {
1582- // Determine target path from DOM or tree
1583- const targetAttrEl = rightScrollRef . current ?. querySelector (
1584- `[data-attr-id="${ reassignHoverTargetId } "]`
1585- ) as HTMLElement | null ;
1586- const tgtPathFromDom =
1587- targetAttrEl ?. dataset ?. entityPath || null ;
1620+ const tgtPathFromDom = reassignHoverTargetEntityPath ;
15881621 const tgtPath =
15891622 tgtPathFromDom ??
15901623 ( ( ) => {
@@ -1923,7 +1956,9 @@ const MappingsView: React.FC = () => {
19231956 setReassignActive ( false ) ;
19241957 setReassignPaths ( [ ] ) ;
19251958 setReassignHoverTargetId ( null ) ;
1959+ setReassignHoverTargetPath ( null ) ;
19261960 reassignHoverTargetIdRef . current = null ;
1961+ reassignHoverTargetPathRef . current = null ;
19271962 pendingReassignRef . current = null ;
19281963 reassignProcessingRef . current = false ;
19291964 }
@@ -1948,7 +1983,9 @@ const MappingsView: React.FC = () => {
19481983 if ( ! reassignActive ) {
19491984 setReassignPaths ( [ ] ) ;
19501985 setReassignHoverTargetId ( null ) ;
1986+ setReassignHoverTargetPath ( null ) ;
19511987 reassignHoverTargetIdRef . current = null ;
1988+ reassignHoverTargetPathRef . current = null ;
19521989 }
19531990 } , [ reassignActive ] ) ;
19541991
@@ -2652,6 +2689,7 @@ const MappingsView: React.FC = () => {
26522689 disableInteractions = { groupId < 0 }
26532690 selectionContext = { {
26542691 selectedTargetAttrId, setSelectedTargetAttrId,
2692+ selectedTargetAttrPath, setSelectedTargetAttrPath,
26552693 selectionIndex, setSelectionIndex,
26562694 selectionAll, setSelectionAll,
26572695 selectedTransformationIds, setSelectedTransformationIds,
@@ -2660,6 +2698,7 @@ const MappingsView: React.FC = () => {
26602698 setReassignTransformations :
26612699 setReassignTransformations as any ,
26622700 reassignHoverTargetId,
2701+ reassignHoverTargetPath,
26632702 prepareReassign : (
26642703 e : React . MouseEvent ,
26652704 transforms : any [ ]
@@ -2703,6 +2742,7 @@ const MappingsView: React.FC = () => {
27032742 disableInteractions = { groupId < 0 }
27042743 selectionContext = { {
27052744 selectedTargetAttrId, setSelectedTargetAttrId,
2745+ selectedTargetAttrPath, setSelectedTargetAttrPath,
27062746 selectionIndex, setSelectionIndex,
27072747 selectionAll, setSelectionAll,
27082748 selectedTransformationIds, setSelectedTransformationIds,
@@ -2711,6 +2751,7 @@ const MappingsView: React.FC = () => {
27112751 setReassignTransformations :
27122752 setReassignTransformations as any ,
27132753 reassignHoverTargetId,
2754+ reassignHoverTargetPath,
27142755 prepareReassign : (
27152756 e : React . MouseEvent ,
27162757 transforms : any [ ]
0 commit comments