@@ -5,6 +5,7 @@ import React, {
55 PropsWithChildren ,
66 useRef ,
77 useEffect ,
8+ useCallback ,
89} from 'react'
910import classNames from 'classnames'
1011import { Photograph , Failure } from '@nutui/icons-react'
@@ -70,6 +71,7 @@ export interface UploaderProps extends BasicComponent {
7071 beforeXhrUpload ?: ( xhr : XMLHttpRequest , options : any ) => void
7172 beforeDelete ?: ( file : FileItem , files : FileItem [ ] ) => boolean
7273 onFileItemClick ?: ( file : FileItem , index : number ) => void
74+ enablePasteUpload ?: boolean
7375}
7476
7577const defaultProps = {
@@ -99,6 +101,7 @@ const defaultProps = {
99101 beforeDelete : ( file : FileItem , files : FileItem [ ] ) => {
100102 return true
101103 } ,
104+ enablePasteUpload : false ,
102105} as UploaderProps
103106
104107const InternalUploader : ForwardRefRenderFunction <
@@ -148,6 +151,7 @@ const InternalUploader: ForwardRefRenderFunction<
148151 beforeUpload,
149152 beforeXhrUpload,
150153 beforeDelete,
154+ enablePasteUpload,
151155 ...restProps
152156 } = { ...defaultProps , ...props }
153157 const [ fileList , setFileList ] = usePropsValue ( {
@@ -307,7 +311,7 @@ const InternalUploader: ForwardRefRenderFunction<
307311 const reader = new FileReader ( )
308312 reader . onload = ( event : ProgressEvent < FileReader > ) => {
309313 fileItem . url = ( event . target as FileReader ) . result as string
310- // setFileList([...fileList, fileItem])
314+ setFileList ( [ ...fileList , fileItem ] )
311315 results . push ( fileItem )
312316 }
313317 reader . readAsDataURL ( file )
@@ -383,6 +387,85 @@ const InternalUploader: ForwardRefRenderFunction<
383387 onFileItemClick ?.( file , index )
384388 }
385389
390+ const handlePaste = useCallback (
391+ ( event : ClipboardEvent ) => {
392+ if ( ! enablePasteUpload || disabled ) return
393+
394+ const clipboardData = event . clipboardData
395+ if ( ! clipboardData ) return
396+
397+ const files : File [ ] = [ ]
398+
399+ if ( clipboardData . items && clipboardData . items . length ) {
400+ for ( let i = 0 ; i < clipboardData . items . length ; i ++ ) {
401+ const item = clipboardData . items [ i ]
402+ if ( item . kind === 'file' && item . type . startsWith ( 'image/' ) ) {
403+ const file = item . getAsFile ( )
404+ if ( file ) {
405+ files . push ( file )
406+ }
407+ }
408+ }
409+ } else if ( clipboardData . files && clipboardData . files . length ) {
410+ for ( let i = 0 ; i < clipboardData . files . length ; i ++ ) {
411+ const file = clipboardData . files [ i ]
412+ if ( file . type . startsWith ( 'image/' ) ) {
413+ files . push ( file )
414+ }
415+ }
416+ }
417+
418+ if ( files . length ) {
419+ if ( beforeUpload ) {
420+ beforeUpload ( files ) . then ( ( f : Array < File > | boolean ) => {
421+ if ( typeof f === 'boolean' ) return
422+
423+ const _files = filterFiles ( new Array < File > ( ) . slice . call ( f ) )
424+ if ( _files . length ) {
425+ readFile ( _files )
426+ onChange ?.( [
427+ ...fileList ,
428+ ...files . map ( ( file ) => ( {
429+ name : file . name ,
430+ type : file . type ,
431+ status : 'ready' ,
432+ } ) ) ,
433+ ] )
434+ }
435+ } )
436+ } else {
437+ const _files = filterFiles ( new Array < File > ( ) . slice . call ( files ) )
438+ if ( _files . length ) {
439+ readFile ( _files )
440+ onChange ?.( [
441+ ...fileList ,
442+ ..._files . map ( ( file ) => ( {
443+ name : file . name ,
444+ type : file . type ,
445+ status : 'ready' ,
446+ } ) ) ,
447+ ] )
448+ }
449+ }
450+ }
451+ } ,
452+ [ enablePasteUpload , disabled , beforeUpload , filterFiles , readFile , onChange ]
453+ )
454+
455+ useEffect ( ( ) => {
456+ fileListRef . current = fileList
457+
458+ if ( enablePasteUpload ) {
459+ document . addEventListener ( 'paste' , handlePaste )
460+ }
461+
462+ return ( ) => {
463+ if ( enablePasteUpload ) {
464+ document . removeEventListener ( 'paste' , handlePaste )
465+ }
466+ }
467+ } , [ fileList , enablePasteUpload , handlePaste ] )
468+
386469 return (
387470 < div className = { classes } { ...restProps } >
388471 { ( children || previewType === 'list' ) && (
0 commit comments