@@ -66,12 +66,46 @@ class FileTree extends React.Component {
6666
6767 this . filePane = React . createRef ( ) ;
6868 this . uploadedFileIdMap = { } ;
69+ this . handleKeyDown = this . handleKeyDown . bind ( this ) ;
70+ }
71+
72+ componentDidMount ( ) {
73+ document . addEventListener ( "keydown" , this . handleKeyDown ) ;
74+ }
75+
76+ componentWillUnmount ( ) {
77+ document . removeEventListener ( "keydown" , this . handleKeyDown ) ;
78+ }
79+
80+ handleKeyDown ( e ) {
81+ if ( e . key === "Delete" && this . state . checkedFiles . length > 0 ) {
82+ e . preventDefault ( ) ;
83+ this . batchDeleteFiles ( ) ;
84+ }
6985 }
7086
7187 UNSAFE_componentWillMount ( ) {
7288 this . getPermissions ( ) ;
7389 }
7490
91+ batchDeleteFiles ( ) {
92+ Modal . confirm ( {
93+ title : i18next . t ( "general:Confirm deletion" ) ,
94+ content : i18next . t ( "store:Are you sure you want to delete the selected items?" ) ,
95+ okText : i18next . t ( "general:OK" ) ,
96+ cancelText : i18next . t ( "general:Cancel" ) ,
97+ onOk : ( ) => {
98+ this . state . checkedFiles . forEach ( file => {
99+ this . deleteFile ( file , file . isLeaf ) ;
100+ } ) ;
101+ this . setState ( {
102+ checkedKeys : [ ] ,
103+ checkedFiles : [ ] ,
104+ } ) ;
105+ } ,
106+ } ) ;
107+ }
108+
75109 getPermissionMap ( permissions ) {
76110 const permissionMap = { } ;
77111 permissions . forEach ( ( permission , index ) => {
@@ -160,6 +194,19 @@ class FileTree extends React.Component {
160194 ) ;
161195 }
162196
197+ uploadFiles ( file , files ) {
198+ const info = {
199+ fileList : Array . from ( files ) . map ( file => {
200+ file . uid = Date . now ( ) + Math . floor ( Math . random ( ) * 1000 ) ;
201+ return {
202+ name : file . name ,
203+ originFileObj : file ,
204+ } ;
205+ } ) ,
206+ } ;
207+ this . uploadFile ( file , info ) ;
208+ }
209+
163210 uploadFile ( file , info ) {
164211 const storeId = `${ this . props . store . owner } /${ this . props . store . name } ` ;
165212
@@ -457,6 +504,34 @@ class FileTree extends React.Component {
457504 tagStyle = { color : "rgba(100,100,100,0.6)" , backgroundColor : "rgba(225,225,225,0.4)" } ;
458505 }
459506
507+ const targetKey = file . isLeaf ? file . parentKey : file . key ;
508+ const isDraggingOver = this . state . dragOverKey === targetKey ;
509+
510+ const handleDragOver = ( e ) => {
511+ e . preventDefault ( ) ;
512+ this . setState ( { dragOverKey : targetKey } ) ;
513+ } ;
514+
515+ const handleDragLeave = ( e ) => {
516+ e . preventDefault ( ) ;
517+ this . setState ( { dragOverKey : null } ) ;
518+ } ;
519+
520+ const handleDrop = ( e ) => {
521+ e . preventDefault ( ) ;
522+ this . setState ( { dragOverKey : null } ) ;
523+
524+ const files = Array . from ( e . dataTransfer . files || [ ] ) ;
525+ if ( files . length === 0 ) { return ; }
526+ if ( ! file . isLeaf ) {
527+ this . uploadFiles ?. ( file , files ) ;
528+ }
529+ } ;
530+ const wrapperStyle = {
531+ background : isDraggingOver ? "rgba(24,144,255,0.15)" : "transparent" ,
532+ transition : "background 0.2s" ,
533+ } ;
534+
460535 if ( file . isLeaf ) {
461536 return (
462537 < Tooltip color = { "rgb(255,255,255,0.8)" } placement = "right" title = {
@@ -525,95 +600,102 @@ class FileTree extends React.Component {
525600 ) ;
526601 } else {
527602 return (
528- < Tooltip color = { "rgb(255,255,255,0.8)" } placement = "right" title = {
529- < div >
530- {
531- ! isWritable ? null : (
532- < React . Fragment >
533- < Tooltip color = { "rgb(255,255,255)" } placement = "top" title = {
534- < span onClick = { ( e ) => e . stopPropagation ( ) } >
535- < div style = { { color : "black" } } >
536- { i18next . t ( "store:New folder" ) } :
537- </ div >
538- < Input . Group style = { { marginTop : "5px" } } compact >
539- < Input style = { { width : "100px" } } value = { this . state . newFolder } onChange = { e => {
540- this . setState ( {
541- newFolder : e . target . value ,
542- } ) ;
543- } } />
544- < Button type = "primary" onClick = { ( e ) => {
545- this . addFile ( file , this . state . newFolder ) ;
603+ < div
604+ style = { wrapperStyle }
605+ onDragOver = { handleDragOver }
606+ onDragLeave = { handleDragLeave }
607+ onDrop = { handleDrop }
608+ >
609+ < Tooltip color = { "rgb(255,255,255,0.8)" } placement = "right" title = {
610+ < div >
611+ {
612+ ! isWritable ? null : (
613+ < React . Fragment >
614+ < Tooltip color = { "rgb(255,255,255)" } placement = "top" title = {
615+ < span onClick = { ( e ) => e . stopPropagation ( ) } >
616+ < div style = { { color : "black" } } >
617+ { i18next . t ( "store:New folder" ) } :
618+ </ div >
619+ < Input . Group style = { { marginTop : "5px" } } compact >
620+ < Input style = { { width : "100px" } } value = { this . state . newFolder } onChange = { e => {
621+ this . setState ( {
622+ newFolder : e . target . value ,
623+ } ) ;
624+ } } />
625+ < Button type = "primary" onClick = { ( e ) => {
626+ this . addFile ( file , this . state . newFolder ) ;
627+ e . stopPropagation ( ) ;
628+ } }
629+ >
630+ OK
631+ </ Button >
632+ </ Input . Group >
633+ </ span >
634+ } >
635+ < span onClick = { ( e ) => e . stopPropagation ( ) } >
636+ < Button style = { { marginRight : "5px" } } icon = { < FolderAddOutlined /> } size = "small" onClick = { ( e ) => {
546637 e . stopPropagation ( ) ;
638+ } } />
639+ </ span >
640+ </ Tooltip >
641+ < Tooltip title = { i18next . t ( "store:Upload file" ) } >
642+ < span onClick = { ( e ) => e . stopPropagation ( ) } >
643+ < Upload directory = { true } multiple = { true } accept = "*" showUploadList = { false } beforeUpload = { file => { return false ; } } onChange = { ( info ) => {
644+ if ( this . checkUploadFile ( info ) ) {
645+ this . setState ( {
646+ isUploadFileModalVisible : true ,
647+ file : file ,
648+ info : info ,
649+ } ) ;
650+ } else {
651+ this . uploadFile ( file , info ) ;
652+ }
547653 } }
548654 >
549- OK
550- </ Button >
551- </ Input . Group >
552- </ span >
553- } >
554- < span onClick = { ( e ) => e . stopPropagation ( ) } >
555- < Button style = { { marginRight : "5px" } } icon = { < FolderAddOutlined /> } size = "small" onClick = { ( e ) => {
556- e . stopPropagation ( ) ;
557- } } />
558- </ span >
559- </ Tooltip >
560- < Tooltip title = { i18next . t ( "store:Upload file" ) } >
561- < span onClick = { ( e ) => e . stopPropagation ( ) } >
562- < Upload multiple = { true } accept = "*" showUploadList = { false } beforeUpload = { file => { return false ; } } onChange = { ( info ) => {
563- if ( this . checkUploadFile ( info ) ) {
564- this . setState ( {
565- isUploadFileModalVisible : true ,
566- file : file ,
567- info : info ,
568- } ) ;
569- } else {
570- this . uploadFile ( file , info ) ;
571- }
572- } }
573- >
574- < Button style = { { marginRight : "5px" } } icon = { < CloudUploadOutlined /> } size = "small" />
575- </ Upload >
576- </ span >
577- </ Tooltip >
578- {
579- file . key === "/" ? null : (
580- < Tooltip title = { i18next . t ( "general:Delete" ) } >
581- < span onClick = { ( e ) => e . stopPropagation ( ) } >
582- < Popconfirm
583- title = { `${ i18next . t ( "general:Sure to delete" ) } : ${ file . title } ?` }
584- onConfirm = { ( e ) => {
585- this . deleteFile ( file , false ) ;
586- } }
587- okText = { i18next . t ( "general:OK" ) }
588- cancelText = { i18next . t ( "general:Cancel" ) }
589- >
590- < Button style = { { marginRight : "5px" } } icon = { < DeleteOutlined /> } size = "small" />
591- </ Popconfirm >
592- </ span >
593- </ Tooltip >
594- )
595- }
596- </ React . Fragment >
597- )
655+ < Button style = { { marginRight : "5px" } } icon = { < CloudUploadOutlined /> } size = "small" />
656+ </ Upload >
657+ </ span >
658+ </ Tooltip >
659+ {
660+ file . key === "/" ? null : (
661+ < Tooltip title = { i18next . t ( "general:Delete" ) } >
662+ < span onClick = { ( e ) => e . stopPropagation ( ) } >
663+ < Popconfirm
664+ title = { `${ i18next . t ( "general:Sure to delete" ) } : ${ file . title } ?` }
665+ onConfirm = { ( e ) => {
666+ this . deleteFile ( file , false ) ;
667+ } }
668+ okText = { i18next . t ( "general:OK" ) }
669+ cancelText = { i18next . t ( "general:Cancel" ) }
670+ >
671+ < Button style = { { marginRight : "5px" } } icon = { < DeleteOutlined /> } size = "small" />
672+ </ Popconfirm >
673+ </ span >
674+ </ Tooltip >
675+ )
676+ }
677+ </ React . Fragment >
678+ )
679+ }
680+ < Tooltip title = { isAdmin ? i18next . t ( "store:Add Permission" ) :
681+ i18next . t ( "store:Apply for Permission" ) } >
682+ < Button icon = { < FileDoneOutlined /> } size = "small" onClick = { ( e ) => {
683+ PermissionUtil . addPermission ( this . props . account , this . props . store , file ) ;
684+ e . stopPropagation ( ) ;
685+ } } />
686+ </ Tooltip >
687+ </ div >
688+ } >
689+ < span style = { tagStyle } >
690+ { file . title }
691+ </ span >
692+
693+
694+ {
695+ ( this . state . permissionMap === null ) ? null : this . renderPermissions ( this . state . permissionMap [ file . key ] , isReadable )
598696 }
599- < Tooltip title = { isAdmin ? i18next . t ( "store:Add Permission" ) :
600- i18next . t ( "store:Apply for Permission" ) } >
601- < Button icon = { < FileDoneOutlined /> } size = "small" onClick = { ( e ) => {
602- PermissionUtil . addPermission ( this . props . account , this . props . store , file ) ;
603- e . stopPropagation ( ) ;
604- } } />
605- </ Tooltip >
606- </ div >
607- } >
608- < span style = { tagStyle } >
609- { file . title }
610- </ span >
611-
612-
613- {
614- ( this . state . permissionMap === null ) ? null : this . renderPermissions ( this . state . permissionMap [ file . key ] , isReadable )
615- }
616- </ Tooltip >
697+ </ Tooltip >
698+ </ div >
617699 ) ;
618700 }
619701 } }
0 commit comments