1
1
import { fromEvent , switchMap , takeUntil , filter , merge , map , tap } from 'rxjs' ;
2
2
import { inRange } from '@tool-pack/basic' ;
3
3
import type { FN } from '@tool-pack/types' ;
4
- import type { RefObject } from 'react' ;
5
4
6
5
export function drag (
7
- ref : RefObject < HTMLDivElement > ,
6
+ refs : ( HTMLElement | undefined ) [ ] ,
8
7
ghostClassName : string ,
9
8
onMove : ( prevIndex : number , currIndex : number ) => void ,
10
9
onUp : ( prevIndex : number , currIndex : number ) => void ,
11
10
) : undefined | FN {
12
- const el = ref . current ;
13
- if ( ! el ) return ;
14
-
15
- const mouseDown$ = fromEvent < MouseEvent > ( el , 'mousedown' ) ;
16
11
const mouseUp$ = fromEvent < MouseEvent > ( window , 'mouseup' ) ;
17
12
const dragOver$ = fromEvent < DragEvent > ( window , 'dragover' ) ;
18
13
const dragEnd$ = fromEvent < DragEvent > ( window , 'dragend' ) ;
19
14
const dragStart$ = fromEvent < DragEvent > ( window , 'dragstart' ) ;
20
15
21
- const queue$ = mouseDown$ . pipe (
22
- map ( findMouseDownChildIndex ) ,
23
- filter ( ( index ) : boolean => index >= 0 ) ,
24
- map (
25
- (
26
- index ,
27
- ) : {
28
- readonly chosen : HTMLElement ;
29
- readonly originIndex : number ;
30
- index : number ;
31
- } => {
32
- const chosen = getChildrenArray ( ) [ index ] as HTMLElement ;
33
- chosen . draggable = true ;
34
- return { originIndex : index , chosen, index } ;
35
- } ,
36
- ) ,
37
- switchMap ( ( params ) =>
38
- dragStart$ . pipe (
39
- tap ( ( e ) : void => {
40
- if ( e . dataTransfer ) e . dataTransfer . dropEffect = 'move' ;
41
- } ) ,
42
- map ( ( ) => params ) ,
16
+ const listen = ( el : HTMLElement ) => {
17
+ const mouseDown$ = fromEvent < MouseEvent > ( el , 'mousedown' ) ;
18
+ const queue$ = mouseDown$ . pipe (
19
+ map ( findMouseDownChildIndex ) ,
20
+ filter ( ( index ) : boolean => index >= 0 ) ,
21
+ map (
22
+ (
23
+ index ,
24
+ ) : {
25
+ readonly chosen : HTMLElement ;
26
+ readonly originIndex : number ;
27
+ index : number ;
28
+ } => {
29
+ const chosen = refs [ index ] as HTMLElement ;
30
+ chosen . draggable = true ;
31
+ return { originIndex : index , chosen, index } ;
32
+ } ,
33
+ ) ,
34
+ switchMap ( ( params ) =>
35
+ dragStart$ . pipe (
36
+ tap ( ( e ) : void => {
37
+ if ( e . dataTransfer ) e . dataTransfer . dropEffect = 'move' ;
38
+ } ) ,
39
+ map ( ( ) => params ) ,
40
+ ) ,
43
41
) ,
44
- ) ,
45
- switchMap ( ( params ) => {
46
- return dragOver$ . pipe (
47
- tap ( ( e : DragEvent ) : void => {
48
- params . chosen . classList . add ( ghostClassName ) ;
49
- if ( e . dataTransfer ) e . dataTransfer . dropEffect = 'move' ;
50
- const currIndex = findDragItemIndex ( e ) ;
51
- if ( currIndex > - 1 && currIndex !== params . index ) {
52
- onMove ( params . index , currIndex ) ;
53
- params . index = currIndex ;
54
- }
55
- if ( mouseInElement ( e , el ) ) e . preventDefault ( ) ;
56
- } ) ,
57
- takeUntil (
58
- merge (
59
- mouseUp$ ,
60
- dragEnd$ . pipe (
61
- tap ( ( e ) : void => {
62
- const currIndex = findDragItemIndex ( e ) ;
63
- if ( currIndex > - 1 && currIndex !== params . originIndex )
64
- onUp ( params . index , currIndex ) ;
42
+ switchMap ( ( params ) => {
43
+ return dragOver$ . pipe (
44
+ tap ( ( e : DragEvent ) : void => {
45
+ params . chosen . classList . add ( ghostClassName ) ;
46
+ if ( e . dataTransfer ) e . dataTransfer . dropEffect = 'move' ;
47
+ const currIndex = findDragItemIndex ( e ) ;
48
+ if ( currIndex > - 1 && currIndex !== params . index ) {
49
+ onMove ( params . index , currIndex ) ;
50
+ params . index = currIndex ;
51
+ }
52
+ if ( mouseInElement ( e , el ) ) e . preventDefault ( ) ;
53
+ } ) ,
54
+ takeUntil (
55
+ merge (
56
+ mouseUp$ ,
57
+ dragEnd$ . pipe (
58
+ tap ( ( e ) : void => {
59
+ const currIndex = findDragItemIndex ( e ) ;
60
+ if ( currIndex > - 1 && currIndex !== params . originIndex )
61
+ onUp ( params . index , currIndex ) ;
62
+ } ) ,
63
+ ) ,
64
+ ) . pipe (
65
+ tap ( ( ) : void => {
66
+ restoreChosenEl ( params . chosen ) ;
65
67
} ) ,
66
68
) ,
67
- ) . pipe (
68
- tap ( ( ) : void => {
69
- restoreChosenEl ( params . chosen ) ;
70
- } ) ,
71
69
) ,
72
- ) ,
73
- ) ;
74
- } ) ,
75
- ) ;
70
+ ) ;
71
+ } ) ,
72
+ ) ;
73
+ const sub = queue$ . subscribe ( ) ;
74
+
75
+ return ( ) => {
76
+ sub . unsubscribe ( ) ;
77
+ } ;
78
+ } ;
76
79
77
- const sub = queue$ . subscribe ( ) ;
80
+ const subs = refs . map < void | FN > ( ( el ) : void | FN => {
81
+ if ( el ) return listen ( el ) ;
82
+ } ) ;
78
83
79
84
return ( ) => {
80
- sub . unsubscribe ( ) ;
85
+ subs . forEach ( ( s ) => s ?.( ) ) ;
86
+ subs . length = 0 ;
81
87
} ;
82
88
83
89
function restoreChosenEl ( el : HTMLElement ) : void {
@@ -87,7 +93,7 @@ export function drag(
87
93
function findMouseDownChildIndex ( e : MouseEvent ) : number {
88
94
const target = e . target as HTMLElement | null ;
89
95
if ( ! target ) return - 1 ;
90
- return getChildrenArray ( ) . findIndex ( ( child ) => child . contains ( target ) ) ;
96
+ return refs . findIndex ( ( child ) => child && child . contains ( target ) ) ;
91
97
}
92
98
function mouseInElement ( ev : MouseEvent , el : HTMLElement ) : boolean {
93
99
const { clientX, clientY } = ev ;
@@ -98,10 +104,6 @@ export function drag(
98
104
) ;
99
105
}
100
106
function findDragItemIndex ( e : DragEvent ) : number {
101
- return getChildrenArray ( ) . findIndex ( ( child ) => mouseInElement ( e , child ) ) ;
102
- }
103
- function getChildrenArray ( ) : HTMLElement [ ] {
104
- if ( ! el ) return [ ] ;
105
- return Array . from ( el . children ) as HTMLElement [ ] ;
107
+ return refs . findIndex ( ( child ) => child && mouseInElement ( e , child ) ) ;
106
108
}
107
109
}
0 commit comments