@@ -16,8 +16,12 @@ const INSIDE_CANVAS = "INSIDE_CANVAS" as const;
1616const OUTSIDE_CANVAS = "OUTSIDE_CANVAS" as const ;
1717const MOUSE_MOVE = "MOUSE_MOVE" as const ;
1818const MOUSE_UP = "MOUSE_UP" as const ;
19+ const MOUSE_DOWN = "MOUSE_DOWN" as const ;
20+ const MOUSE_OVER = "MOUSE_OVER" as const ;
1921const COMPONENT_CREATED = "COMPONENT_CREATED" as const ; // emitted only after drag-drop
2022const SCROLL = "SCROLL" as const ;
23+ const BLUR = "BLUR" as const ;
24+ const COMPONENT_RENDERED = "COMPONENT_RENDERED" as const ;
2125
2226type IFRAME_DETECTED_EVENT = { type : typeof IFRAME_DETECTED } ;
2327type TOP_WINDOW_DETECTED_EVENT = { type : typeof TOP_WINDOW_DETECTED } ;
@@ -46,6 +50,14 @@ type MOUSE_UP_EVENT = {
4650 type : typeof MOUSE_UP ;
4751 event : { pageX : number ; pageY : number ; target : MouseEvent [ "target" ] } ;
4852} ;
53+ type MOUSE_DOWN_EVENT = {
54+ type : typeof MOUSE_DOWN ;
55+ event : { pageX : number ; pageY : number ; target : MouseEvent [ "target" ] } ;
56+ } ;
57+ type MOUSE_OVER_EVENT = {
58+ type : typeof MOUSE_OVER ;
59+ event : { pageX : number ; pageY : number ; target : MouseEvent [ "target" ] } ;
60+ } ;
4961type COMPONENT_CREATED_EVENT = {
5062 type : typeof COMPONENT_CREATED ;
5163 compId : string ;
@@ -55,6 +67,13 @@ type COMPONENT_CREATED_EVENT = {
5567type SCROLL_EVENT = {
5668 type : typeof SCROLL ;
5769} ;
70+ type BLUR_EVENT = {
71+ type : typeof BLUR ;
72+ } ;
73+ type COMPONENT_RENDERED_EVENT = {
74+ type : typeof COMPONENT_RENDERED ;
75+ compId : string ;
76+ } ;
5877
5978type CanvasMachineEvent =
6079 | IFRAME_DETECTED_EVENT
@@ -66,8 +85,12 @@ type CanvasMachineEvent =
6685 | OUTSIDE_CANVAS_EVENT
6786 | MOUSE_MOVE_EVENT
6887 | MOUSE_UP_EVENT
88+ | MOUSE_DOWN_EVENT
89+ | MOUSE_OVER_EVENT
6990 | COMPONENT_CREATED_EVENT
70- | SCROLL_EVENT ;
91+ | SCROLL_EVENT
92+ | BLUR_EVENT
93+ | COMPONENT_RENDERED_EVENT ;
7194
7295// states
7396const initial = "initial" as const ;
@@ -80,6 +103,12 @@ const drag_in_progress_active = "drag_in_progress_active" as const;
80103// inside ready
81104const idle = "idle" as const ;
82105const hover = "hover" as const ;
106+ const pressed = "pressed" as const ;
107+ const selected = "selected" as const ;
108+ const focused = "focused" as const ;
109+ const unfocused = "unfocused" as const ;
110+ const selectIdle = "selectIdle" as const ;
111+ const hoverWhileSelected = "hoverWhileSelected" as const ;
83112
84113// context
85114
@@ -94,6 +123,8 @@ type CanvasMachineContext = {
94123 target : MouseEvent [ "target" ] ;
95124 } | null ;
96125 hovered : string | null ;
126+ selected : string | null ;
127+ lastDropped : string | null ; // string until COMPONENT_RENDERED received, otherwise null
97128} ;
98129
99130// actions
@@ -108,7 +139,7 @@ function setDragData(
108139
109140function setMousePosition (
110141 context : CanvasMachineContext ,
111- event : MOUSE_MOVE_EVENT | MOUSE_UP_EVENT
142+ event : MOUSE_MOVE_EVENT | MOUSE_UP_EVENT | MOUSE_DOWN_EVENT
112143) {
113144 context . mousePosition = event . event ;
114145}
@@ -127,11 +158,37 @@ function setHoverComponent(
127158 }
128159}
129160
161+ function setSelectedComponent (
162+ context : CanvasMachineContext ,
163+ event : MOUSE_DOWN_EVENT
164+ ) {
165+ const { target } = event . event ;
166+ if ( target !== null && "closest" in target ) {
167+ const comp = ( target as any ) . closest ( "[data-atri-comp-id]" ) ;
168+ if ( comp !== null ) {
169+ const compId = comp . getAttribute ( "data-atri-comp-id" ) ;
170+ context . selected = compId ;
171+ }
172+ }
173+ }
174+
175+ function setLastDropped (
176+ context : CanvasMachineContext ,
177+ event : COMPONENT_CREATED_EVENT
178+ ) {
179+ context . lastDropped = event . compId ;
180+ }
181+
182+ function handleComponentRendered ( context : CanvasMachineContext ) {
183+ context . selected = context . lastDropped ;
184+ context . lastDropped = null ;
185+ }
186+
130187// conds
131188
132189function insideComponent (
133190 _context : CanvasMachineContext ,
134- event : MOUSE_MOVE_EVENT
191+ event : MOUSE_MOVE_EVENT | MOUSE_DOWN_EVENT
135192) {
136193 const { target } = event . event ;
137194 if ( target !== null && "closest" in target ) {
@@ -158,6 +215,28 @@ function hoveringOverDifferentComponent(
158215 return false ;
159216}
160217
218+ function selectedDifferentComponent (
219+ context : CanvasMachineContext ,
220+ event : MOUSE_DOWN_EVENT | MOUSE_OVER_EVENT
221+ ) {
222+ const { target } = event . event ;
223+ if ( target !== null && "closest" in target ) {
224+ const comp = ( target as any ) . closest ( "[data-atri-comp-id]" ) ;
225+ if ( comp !== null ) {
226+ const compId = comp . getAttribute ( "data-atri-comp-id" ) ;
227+ return compId !== context . selected ;
228+ }
229+ }
230+ return false ;
231+ }
232+
233+ function isLastDroppedComponent (
234+ context : CanvasMachineContext ,
235+ event : COMPONENT_RENDERED_EVENT
236+ ) {
237+ return context . lastDropped === event . compId ;
238+ }
239+
161240type Callback = (
162241 context : CanvasMachineContext ,
163242 event : CanvasMachineEvent
@@ -170,7 +249,10 @@ type SubscribeStates =
170249 | typeof OUTSIDE_CANVAS
171250 | typeof COMPONENT_CREATED
172251 | "hover"
173- | "hoverEnd" ;
252+ | "hoverEnd"
253+ | "focus"
254+ | "focusEnd"
255+ | typeof COMPONENT_RENDERED ;
174256
175257export function createCanvasMachine ( id : string ) {
176258 const subscribers : { [ key in SubscribeStates ] : Callback [ ] } = {
@@ -182,6 +264,9 @@ export function createCanvasMachine(id: string) {
182264 [ COMPONENT_CREATED ] : [ ] ,
183265 hover : [ ] ,
184266 hoverEnd : [ ] ,
267+ focus : [ ] ,
268+ focusEnd : [ ] ,
269+ COMPONENT_RENDERED : [ ] ,
185270 } ;
186271 function subscribeCanvasMachine ( state : SubscribeStates , cb : Callback ) {
187272 subscribers [ state ] . push ( cb ) ;
@@ -192,6 +277,7 @@ export function createCanvasMachine(id: string) {
192277 }
193278 } ;
194279 }
280+
195281 function callSubscribers (
196282 state : SubscribeStates ,
197283 context : CanvasMachineContext ,
@@ -215,6 +301,7 @@ export function createCanvasMachine(id: string) {
215301 callSubscribers ( state , context , event ) ;
216302 } ;
217303 }
304+
218305 const canvasMachine = createMachine < CanvasMachineContext , CanvasMachineEvent > (
219306 {
220307 id,
@@ -227,6 +314,8 @@ export function createCanvasMachine(id: string) {
227314 dragData : null ,
228315 mousePosition : null ,
229316 hovered : null ,
317+ selected : null ,
318+ lastDropped : null ,
230319 } ,
231320 states : {
232321 [ initial ] : {
@@ -250,28 +339,102 @@ export function createCanvasMachine(id: string) {
250339 cond : insideComponent ,
251340 actions : [ "setHoverComponent" ] ,
252341 } ,
342+ [ COMPONENT_RENDERED ] : {
343+ target : selected ,
344+ cond : isLastDroppedComponent ,
345+ actions : [ "handleComponentRendered" , "emitComponentRendered" ] ,
346+ } ,
253347 } ,
254348 } ,
255349 [ hover ] : {
350+ entry : ( context , event ) => {
351+ callSubscribers ( "hover" , context , event ) ;
352+ } ,
353+ exit : ( context , event ) => {
354+ context . hovered = null ;
355+ callSubscribers ( "hoverEnd" , context , event ) ;
356+ } ,
256357 on : {
257358 [ MOUSE_MOVE ] : {
258359 target : hover ,
259360 cond : hoveringOverDifferentComponent ,
260361 actions : [ "setHoverComponent" ] ,
261362 } ,
363+ [ MOUSE_DOWN ] : {
364+ target : pressed ,
365+ } ,
262366 [ SCROLL ] : {
263367 target : idle ,
264368 } ,
265369 [ OUTSIDE_CANVAS ] : {
266370 target : idle ,
267371 } ,
268372 } ,
269- entry : ( context , event ) => {
270- callSubscribers ( "hover" , context , event ) ;
373+ } ,
374+ [ pressed ] : {
375+ on : {
376+ [ MOUSE_UP ] : {
377+ target : selected ,
378+ actions : [ "setSelectedComponent" ] ,
379+ } ,
271380 } ,
272- exit : ( context , event ) => {
273- context . hovered = null ;
274- callSubscribers ( "hoverEnd" , context , event ) ;
381+ } ,
382+ [ selected ] : {
383+ exit : ( context ) => {
384+ context . selected = null ;
385+ } ,
386+ on : {
387+ [ MOUSE_DOWN ] : {
388+ target : pressed ,
389+ cond : selectedDifferentComponent ,
390+ } ,
391+ } ,
392+ type : "parallel" ,
393+ states : {
394+ focusstates : {
395+ initial : focused ,
396+ states : {
397+ [ focused ] : {
398+ entry : ( context , event ) => {
399+ callSubscribers ( "focus" , context , event ) ;
400+ } ,
401+ exit : ( context , event ) => {
402+ callSubscribers ( "focusEnd" , context , event ) ;
403+ } ,
404+ on : {
405+ [ BLUR ] : {
406+ target : unfocused ,
407+ } ,
408+ } ,
409+ } ,
410+ [ unfocused ] : {
411+ type : "final" ,
412+ } ,
413+ } ,
414+ } ,
415+ hoverstates : {
416+ initial : selectIdle ,
417+ states : {
418+ [ selectIdle ] : {
419+ on : {
420+ [ MOUSE_OVER ] : {
421+ target : hoverWhileSelected ,
422+ cond : selectedDifferentComponent ,
423+ } ,
424+ } ,
425+ } ,
426+ [ hoverWhileSelected ] : {
427+ on : {
428+ [ MOUSE_OVER ] : {
429+ target : hoverWhileSelected ,
430+ cond : selectedDifferentComponent ,
431+ } ,
432+ [ SCROLL ] : { target : selectIdle } ,
433+ [ OUTSIDE_CANVAS ] : { target : selectIdle } ,
434+ } ,
435+ } ,
436+ } ,
437+ } ,
275438 } ,
276439 } ,
277440 } ,
@@ -281,7 +444,7 @@ export function createCanvasMachine(id: string) {
281444 actions : [ "setDragData" ] ,
282445 } ,
283446 [ COMPONENT_CREATED ] : {
284- actions : [ "emitComponentCreated" ] ,
447+ actions : [ "emitComponentCreated" , "setLastDropped" ] ,
285448 } ,
286449 } ,
287450 } ,
@@ -327,7 +490,11 @@ export function createCanvasMachine(id: string) {
327490 emitInsideCanvas : callSubscribersFromAction ( "INSIDE_CANVAS" ) ,
328491 emitReady : callSubscribersFromAction ( "ready" ) ,
329492 emitComponentCreated : callSubscribersFromAction ( "COMPONENT_CREATED" ) ,
493+ emitComponentRendered : callSubscribersFromAction ( "COMPONENT_RENDERED" ) ,
330494 setHoverComponent,
495+ setSelectedComponent,
496+ setLastDropped,
497+ handleComponentRendered,
331498 } ,
332499 }
333500 ) ;
0 commit comments