@@ -3,8 +3,11 @@ import useIntersectionTop from "@/functions/useIntersectionTop";
3
3
import { _STARTER_ } from "@/parser/OrderedParticipants" ;
4
4
import { PARTICIPANT_HEIGHT } from "@/positioning/Constants" ;
5
5
import {
6
+ codeAtom ,
6
7
diagramElementAtom ,
8
+ dragParticipantAtom ,
7
9
modeAtom ,
10
+ onContentChangeAtom ,
8
11
onSelectAtom ,
9
12
participantsAtom ,
10
13
RenderMode ,
@@ -14,18 +17,21 @@ import {
14
17
import { cn } from "@/utils" ;
15
18
import { brightnessIgnoreAlpha , removeAlpha } from "@/utils/Color" ;
16
19
import { getElementDistanceToTop } from "@/utils/dom" ;
17
- import { useAtomValue , useSetAtom } from "jotai" ;
20
+ import { useAtom , useAtomValue , useSetAtom } from "jotai" ;
18
21
import { useMemo , useRef } from "react" ;
19
22
import { ParticipantLabel } from "./ParticipantLabel" ;
20
23
import iconPath from "../../Tutorial/Icons" ;
21
24
22
25
const INTERSECTION_ERROR_MARGIN = 10 ;
23
26
27
+ const formatName = ( name : string ) => ( name . includes ( " " ) ? `"${ name } "` : name ) ;
28
+
24
29
export const Participant = ( props : {
25
30
entity : Record < string , string > ;
26
31
offsetTop2 ?: number ;
27
32
} ) => {
28
33
const elRef = useRef < HTMLDivElement > ( null ) ;
34
+ const [ code , setCode ] = useAtom ( codeAtom ) ;
29
35
const mode = useAtomValue ( modeAtom ) ;
30
36
const participants = useAtomValue ( participantsAtom ) ;
31
37
const diagramElement = useAtomValue ( diagramElementAtom ) ;
@@ -34,6 +40,8 @@ export const Participant = (props: {
34
40
const onSelect = useSetAtom ( onSelectAtom ) ;
35
41
const intersectionTop = useIntersectionTop ( ) ;
36
42
const [ scrollTop ] = useDocumentScroll ( ) ;
43
+ const [ dragParticipant , setDragParticipant ] = useAtom ( dragParticipantAtom ) ;
44
+ const onContentChange = useAtomValue ( onContentChangeAtom ) ;
37
45
38
46
const isDefaultStarter = props . entity . name === _STARTER_ ;
39
47
@@ -89,51 +97,89 @@ export const Participant = (props: {
89
97
? iconPath [ "actor" ]
90
98
: iconPath [ props . entity . type ?. toLowerCase ( ) as "actor" ] ;
91
99
100
+ const handleDrag = ( ) => {
101
+ const {
102
+ left = 0 ,
103
+ top = 0 ,
104
+ width = 0 ,
105
+ height = 0 ,
106
+ } = elRef . current ?. getBoundingClientRect ( ) || { } ;
107
+ const diagramRect = diagramElement ?. getBoundingClientRect ( ) ;
108
+ setDragParticipant ( {
109
+ name : props . entity . name ,
110
+ x : left + width / 2 - ( diagramRect ?. left || 0 ) ,
111
+ y : top + height / 2 - ( diagramRect ?. top || 0 ) ,
112
+ } ) ;
113
+ } ;
114
+ const handleDrop = ( ) => {
115
+ if ( dragParticipant && dragParticipant . name !== props . entity . name ) {
116
+ const isFromStarter = dragParticipant . name === _STARTER_ ;
117
+ const newCode =
118
+ code +
119
+ ( isFromStarter
120
+ ? `\n${ formatName ( props . entity . name ) } .message`
121
+ : `\n${ formatName ( dragParticipant . name ) } -> ${ formatName ( props . entity . name ) } .message` ) ;
122
+ setCode ( newCode ) ;
123
+ onContentChange ( newCode ) ;
124
+ }
125
+ } ;
126
+
92
127
return (
93
128
< div
94
129
className = { cn (
95
- "participant bg-skin-participant shadow-participant border-skin-participant text-skin-participant rounded text-base leading-4 flex flex-col justify-center z-10 h-10 top-8" ,
96
- { selected : selected . includes ( props . entity . name ) } ,
130
+ "hover:shadow-[0_0_3px_2px_#0094D988] hover:shadow-participant-hover transition-shadow duration-200 cursor-pointer rounded" ,
131
+ dragParticipant &&
132
+ dragParticipant . name !== props . entity . name &&
133
+ "cursor-copy" ,
97
134
) }
98
- ref = { elRef }
99
- style = { {
100
- backgroundColor : isDefaultStarter ? undefined : backgroundColor ,
101
- color : isDefaultStarter ? undefined : color ,
102
- transform : `translateY(${ stickyVerticalOffset } px)` ,
103
- } }
104
- onClick = { ( ) => onSelect ( props . entity . name ) }
105
- data-participant-id = { props . entity . name }
106
135
>
107
- < div className = "flex items-center justify-center" >
108
- { icon && (
109
- < div
110
- className = "h-6 w-6 mr-1 flex-shrink-0 [&>svg]:w-full [&>svg]:h-full"
111
- aria-description = { `icon for ${ props . entity . name } ` }
112
- dangerouslySetInnerHTML = { {
113
- __html : icon ,
114
- } }
115
- />
136
+ < div
137
+ className = { cn (
138
+ "participant bg-skin-participant shadow-participant border-skin-participant text-skin-participant rounded text-base leading-4 flex flex-col justify-center z-10 h-10 top-8" ,
139
+ { selected : selected . includes ( props . entity . name ) } ,
116
140
) }
117
-
118
- { ! isDefaultStarter && (
119
- < div className = "h-5 group flex flex-col justify-center" >
120
- { props . entity . stereotype && (
121
- < label className = "interface leading-4" >
122
- «{ props . entity . stereotype } »
123
- </ label >
124
- ) }
125
- < ParticipantLabel
126
- labelText = {
127
- props . entity . assignee
128
- ? props . entity . name . split ( ":" ) [ 1 ]
129
- : props . entity . label || props . entity . name
130
- }
131
- labelPositions = { labelPositions }
132
- assignee = { props . entity . assignee }
133
- assigneePositions = { assigneePositions }
141
+ ref = { elRef }
142
+ style = { {
143
+ backgroundColor : isDefaultStarter ? undefined : backgroundColor ,
144
+ color : isDefaultStarter ? undefined : color ,
145
+ transform : `translateY(${ stickyVerticalOffset } px)` ,
146
+ } }
147
+ onClick = { ( ) => onSelect ( props . entity . name ) }
148
+ data-participant-id = { props . entity . name }
149
+ onMouseDown = { handleDrag }
150
+ onMouseUp = { handleDrop }
151
+ >
152
+ < div className = "flex items-center justify-center" >
153
+ { icon && (
154
+ < div
155
+ className = "h-6 w-6 mr-1 flex-shrink-0 [&>svg]:w-full [&>svg]:h-full"
156
+ aria-description = { `icon for ${ props . entity . name } ` }
157
+ dangerouslySetInnerHTML = { {
158
+ __html : icon ,
159
+ } }
134
160
/>
135
- </ div >
136
- ) }
161
+ ) }
162
+
163
+ { ! isDefaultStarter && (
164
+ < div className = "h-5 group flex flex-col justify-center" >
165
+ { props . entity . stereotype && (
166
+ < label className = "interface leading-4" >
167
+ «{ props . entity . stereotype } »
168
+ </ label >
169
+ ) }
170
+ < ParticipantLabel
171
+ labelText = {
172
+ props . entity . assignee
173
+ ? props . entity . name . split ( ":" ) [ 1 ]
174
+ : props . entity . label || props . entity . name
175
+ }
176
+ labelPositions = { labelPositions }
177
+ assignee = { props . entity . assignee }
178
+ assigneePositions = { assigneePositions }
179
+ />
180
+ </ div >
181
+ ) }
182
+ </ div >
137
183
</ div >
138
184
</ div >
139
185
) ;
0 commit comments