1
- import { useEffect , useState } from 'react' ;
1
+ import { useEffect , useRef , useState } from 'react' ;
2
2
import { getElement , isTarget } from '@/utils/helpers' ;
3
3
import { useRefState } from '../useRefState/useRefState' ;
4
4
/**
@@ -12,40 +12,40 @@ import { useRefState } from '../useRefState/useRefState';
12
12
* @param {DataTypes } [options.dataTypes] The data types
13
13
* @param {boolean } [options.multiple] The multiple mode
14
14
* @param {(files: File[] | null, event: DragEvent) => void } [options.onDrop] The on drop callback function
15
- * @param {(files: File[] | null, event: DragEvent) => void } [options.onEnter] The on enter callback function
16
- * @param {(files: File[] | null, event: DragEvent) => void } [options.onLeave] The on leave callback function
17
- * @param {(files: File[] | null, event: DragEvent) => void } [options.onOver] The on over callback function
15
+ * @param {(event: DragEvent) => void } [options.onEnter] The on enter callback function
16
+ * @param {(event: DragEvent) => void } [options.onLeave] The on leave callback function
17
+ * @param {(event: DragEvent) => void } [options.onOver] The on over callback function
18
18
* @returns {[boolean, File[] | null] } The object with drop zone states
19
19
*
20
20
* @example
21
- * const {isOver , files} = useDropZone(ref, options);
21
+ * const { overed , files } = useDropZone(ref, options);
22
22
*
23
23
* @overload
24
24
* @param {Target } target The target element drop zone's
25
25
* @param {(files: File[] | null, event: DragEvent) => void } [callback] The callback function to be invoked on drop
26
26
* @returns {[boolean, File[] | null] } The object with drop zone states
27
27
*
28
28
* @example
29
- * const {isOver , files} = useDropZone(ref, () => console.log('callback'));
29
+ * const { overed , files } = useDropZone(ref, () => console.log('callback'));
30
30
*
31
31
* @overload
32
32
* @param {DataTypes } [options.dataTypes] The data types
33
33
* @param {boolean } [options.multiple] The multiple mode
34
34
* @param {(files: File[] | null, event: DragEvent) => void } [options.onDrop] The on drop callback function
35
- * @param {(files: File[] | null, event: DragEvent) => void } [options.onEnter] The on enter callback function
36
- * @param {(files: File[] | null, event: DragEvent) => void } [options.onLeave] The on leave callback function
37
- * @param {(files: File[] | null, event: DragEvent) => void } [options.onOver] The on over callback function
35
+ * @param {(event: DragEvent) => void } [options.onEnter] The on enter callback function
36
+ * @param {(event: DragEvent) => void } [options.onLeave] The on leave callback function
37
+ * @param {(event: DragEvent) => void } [options.onOver] The on over callback function
38
38
* @returns {[StateRef<Target>, boolean, File[] | null] } The object with drop zone states and ref
39
39
*
40
40
* @example
41
- * const { ref, isOver , files } = useDropZone(options);
41
+ * const { ref, overed , files } = useDropZone(options);
42
42
*
43
43
* @overload
44
44
* @param {(files: File[] | null, event: DragEvent) => void } [callback] The callback function to be invoked on drop
45
45
* @returns {[StateRef<Target>, boolean, File[] | null] } The object with drop zone states and ref
46
46
*
47
47
* @example
48
- * const { ref, isOver , files } = useDropZone(() => console.log('callback'));
48
+ * const { ref, overed , files } = useDropZone(() => console.log('callback'));
49
49
*/
50
50
export const useDropZone = ( ...params ) => {
51
51
const target = isTarget ( params [ 0 ] ) ? params [ 0 ] : undefined ;
@@ -57,72 +57,85 @@ export const useDropZone = (...params) => {
57
57
? params [ 0 ]
58
58
: { onDrop : params [ 0 ] } ;
59
59
const internalRef = useRefState ( ) ;
60
+ const counterRef = useRef ( 0 ) ;
60
61
const [ files , setFiles ] = useState ( null ) ;
61
- const [ isOver , setIsOver ] = useState ( false ) ;
62
+ const [ overed , setOvered ] = useState ( false ) ;
63
+ const dataTypes = options . dataTypes ;
62
64
const getFiles = ( event ) => {
63
- const list = Array . from ( event . dataTransfer ?. files ?? [ ] ) ;
64
- return list . length === 0 ? null : options . multiple ? list : [ list [ 0 ] ] ;
65
+ if ( ! event . dataTransfer ) return null ;
66
+ const list = Array . from ( event . dataTransfer . files ) ;
67
+ if ( options . multiple ) return list ;
68
+ if ( ! list . length ) return null ;
69
+ return [ list [ 0 ] ] ;
65
70
} ;
66
71
const checkDataTypes = ( types ) => {
67
- const dataTypes = options . dataTypes ;
72
+ if ( ! dataTypes ) return true ;
68
73
if ( typeof dataTypes === 'function' ) return dataTypes ( types ) ;
69
- if ( ! dataTypes ?. length ) return true ;
70
- if ( types . length === 0 ) return false ;
71
- return types . every ( ( type ) => dataTypes ?. some ( ( dataType ) => type . includes ( dataType ) ) ) ;
74
+ if ( ! dataTypes . length ) return true ;
75
+ if ( ! types . length ) return false ;
76
+ return types . every ( ( type ) => {
77
+ console . log ( 'type' , type ) ;
78
+ console . log ( 'dataTypes' , dataTypes ) ;
79
+ return dataTypes . some ( ( dataType ) => type . includes ( dataType ) ) ;
80
+ } ) ;
72
81
} ;
73
82
const checkValidity = ( items ) => {
74
- const types = Array . from ( items ?? [ ] ) . map ( ( item ) => item . type ) ;
83
+ const types = Array . from ( items ) . map ( ( item ) => item . type ) ;
75
84
const dataTypesValid = checkDataTypes ( types ) ;
76
85
const multipleFilesValid = options . multiple || items . length <= 1 ;
77
86
return dataTypesValid && multipleFilesValid ;
78
87
} ;
79
- const handleDragEvent = ( event , eventType ) => {
80
- const dataTransferItemList = event . dataTransfer ?. items ;
81
- const isValid = ( dataTransferItemList && checkValidity ( dataTransferItemList ) ) ?? false ;
82
- if ( ! isValid ) {
83
- if ( event . dataTransfer ) event . dataTransfer . dropEffect = 'none' ;
84
- return ;
85
- }
86
- event . preventDefault ( ) ;
87
- if ( event . dataTransfer ) event . dataTransfer . dropEffect = 'copy' ;
88
- const currentFiles = getFiles ( event ) ;
89
- if ( eventType === 'drop' ) {
90
- setIsOver ( false ) ;
91
- setFiles ( currentFiles ) ;
92
- options . onDrop ?. ( currentFiles , event ) ;
93
- return ;
94
- }
95
- if ( eventType === 'enter' ) {
96
- setIsOver ( true ) ;
97
- options . onEnter ?. ( null , event ) ;
98
- return ;
99
- }
100
- if ( eventType === 'leave' ) {
101
- setIsOver ( false ) ;
102
- options . onLeave ?. ( null , event ) ;
103
- return ;
104
- }
105
- if ( eventType === 'over' ) options . onOver ?. ( null , event ) ;
106
- } ;
107
88
useEffect ( ( ) => {
108
89
if ( ! target && ! internalRef . state ) return ;
109
90
const element = target ? getElement ( target ) : internalRef . current ;
110
91
if ( ! element ) return ;
111
- const handleDrop = ( event ) => handleDragEvent ( event , 'drop' ) ;
112
- const handleDragOver = ( event ) => handleDragEvent ( event , 'over' ) ;
113
- const handleDragEnter = ( event ) => handleDragEvent ( event , 'enter' ) ;
114
- const handleDragLeave = ( event ) => handleDragEvent ( event , 'leave' ) ;
115
- element . addEventListener ( 'dragenter' , handleDragEnter ) ;
116
- element . addEventListener ( 'dragover' , handleDragOver ) ;
117
- element . addEventListener ( 'dragleave' , handleDragLeave ) ;
118
- element . addEventListener ( 'drop' , handleDrop ) ;
92
+ const onEvent = ( event , type ) => {
93
+ if ( ! event . dataTransfer ) return ;
94
+ const isValid = checkValidity ( event . dataTransfer . items ) ;
95
+ if ( ! isValid ) {
96
+ event . dataTransfer . dropEffect = 'none' ;
97
+ return ;
98
+ }
99
+ event . preventDefault ( ) ;
100
+ event . dataTransfer . dropEffect = 'copy' ;
101
+ const currentFiles = getFiles ( event ) ;
102
+ if ( type === 'drop' ) {
103
+ counterRef . current = 0 ;
104
+ setOvered ( false ) ;
105
+ setFiles ( currentFiles ) ;
106
+ options . onDrop ?. ( currentFiles , event ) ;
107
+ return ;
108
+ }
109
+ if ( type === 'enter' ) {
110
+ counterRef . current += 1 ;
111
+ setOvered ( true ) ;
112
+ options . onEnter ?. ( event ) ;
113
+ return ;
114
+ }
115
+ if ( type === 'leave' ) {
116
+ counterRef . current -= 1 ;
117
+ if ( counterRef . current !== 0 ) return ;
118
+ setOvered ( false ) ;
119
+ options . onLeave ?. ( event ) ;
120
+ return ;
121
+ }
122
+ if ( type === 'over' ) options . onOver ?. ( event ) ;
123
+ } ;
124
+ const onDrop = ( event ) => onEvent ( event , 'drop' ) ;
125
+ const onDragOver = ( event ) => onEvent ( event , 'over' ) ;
126
+ const onDragEnter = ( event ) => onEvent ( event , 'enter' ) ;
127
+ const onDragLeave = ( event ) => onEvent ( event , 'leave' ) ;
128
+ element . addEventListener ( 'dragenter' , onDragEnter ) ;
129
+ element . addEventListener ( 'dragover' , onDragOver ) ;
130
+ element . addEventListener ( 'dragleave' , onDragLeave ) ;
131
+ element . addEventListener ( 'drop' , onDrop ) ;
119
132
return ( ) => {
120
- element . removeEventListener ( 'dragenter' , handleDragEnter ) ;
121
- element . removeEventListener ( 'dragover' , handleDragOver ) ;
122
- element . removeEventListener ( 'dragleave' , handleDragLeave ) ;
123
- element . removeEventListener ( 'drop' , handleDrop ) ;
133
+ element . removeEventListener ( 'dragenter' , onDragEnter ) ;
134
+ element . removeEventListener ( 'dragover' , onDragOver ) ;
135
+ element . removeEventListener ( 'dragleave' , onDragLeave ) ;
136
+ element . removeEventListener ( 'drop' , onDrop ) ;
124
137
} ;
125
138
} , [ target , internalRef . current ] ) ;
126
- if ( target ) return { isOver , files } ;
127
- return { ref : internalRef , isOver , files } ;
139
+ if ( target ) return { overed , files } ;
140
+ return { ref : internalRef , overed , files } ;
128
141
} ;
0 commit comments