1- /* eslint-disable @typescript-eslint/no-use-before-define */
2- import * as React from 'react' ;
3- import * as _ from 'lodash-es' ;
4- import { NativeTypes } from 'react-dnd-html5-backend' ;
5- import { DropTarget } from 'react-dnd' ;
1+ import { useState , useCallback } from 'react' ;
62import { ResourceYAMLEditorProps } from '@console/dynamic-plugin-sdk' ;
3+ import { isText } from 'istextorbinary' ;
74
85import { EditYAML } from './edit-yaml' ;
9- import withDragDropContext from './utils/drag-drop-context' ;
10- import { DropTargetMonitor } from 'react-dnd/lib/interfaces' ;
11- import * as ITOB from 'istextorbinary' ;
6+ import {
7+ DropEvent ,
8+ DropzoneErrorCode ,
9+ MultipleFileUpload ,
10+ MultipleFileUploadProps ,
11+ } from '@patternfly/react-core' ;
12+ import { useTranslation } from 'react-i18next' ;
13+ import { units } from './utils' ;
1214
1315// Maximal file size, in bytes, that user can upload
1416const maxFileUploadSize = 4000000 ;
15- const fileSizeErrorMsg = 'Maximum file size exceeded. File limit is 4MB.' ;
16- const fileTypeErrorMsg = 'Binary file detected. Edit text based YAML files only.' ;
17-
18- const boxTarget = {
19- drop ( props , monitor ) {
20- if ( props . onDrop && monitor . isOver ( ) ) {
21- props . onDrop ( props , monitor ) ;
22- }
23- } ,
24- } ;
2517
26- const EditYAMLComponent = DropTarget ( NativeTypes . FILE , boxTarget , ( connectObj , monitor ) => ( {
27- connectDropTarget : connectObj . dropTarget ( ) ,
28- isOver : monitor . isOver ( ) ,
29- canDrop : monitor . canDrop ( ) ,
30- } ) ) ( EditYAML as React . FC < EditYAMLProps > ) ;
18+ export type DroppedFile = {
19+ error ?: string ;
20+ id : string ;
21+ name : string ;
22+ size : number ;
23+ } ;
3124
3225type DroppableEditYAMLProps = ResourceYAMLEditorProps & {
3326 allowMultiple ?: boolean ;
3427 isCodeImportRedirect ?: boolean ;
3528} ;
3629
30+ const useDropErrorMessage = ( ) : ( ( errorCode : DropzoneErrorCode , fileName : string ) => string ) => {
31+ const { t } = useTranslation ( 'public' ) ;
32+
33+ return useCallback (
34+ ( errorCode , fileName ) => {
35+ switch ( errorCode ) {
36+ case 'file-too-large' :
37+ return t (
38+ 'Ignoring {{ fileName }}: Maximum file size exceeded. File limit is {{ size }}.' ,
39+ {
40+ fileName,
41+ size : units . humanize ( maxFileUploadSize , 'decimalBytes' , true ) . string ,
42+ } ,
43+ ) ;
44+ case 'too-many-files' :
45+ return t ( 'Ignoring {{ fileName }}: Too many files. Maximum one file can be uploaded.' , {
46+ fileName,
47+ } ) ;
48+ case 'file-invalid-type' :
49+ return t (
50+ 'Ignoring {{ fileName }}: Invalid file type. Only text based YAML files are supported.' ,
51+ {
52+ fileName,
53+ } ,
54+ ) ;
55+ default :
56+ return t ( 'An error occurred while uploading {{ fileName }}.' , {
57+ fileName,
58+ } ) ;
59+ }
60+ } ,
61+ [ t ] ,
62+ ) ;
63+ } ;
64+
65+ export const DroppableEditYAML : React . FCC < DroppableEditYAMLProps > = ( {
66+ allowMultiple,
67+ initialResource,
68+ create = false ,
69+ onChange = ( ) => null ,
70+ hideHeader = false ,
71+ isCodeImportRedirect = false ,
72+ ...props
73+ } ) => {
74+ const { t } = useTranslation ( 'public' ) ;
75+ const getDropErrorMessage = useDropErrorMessage ( ) ;
76+
77+ const [ fileUpload , setFileUpload ] = useState ( '' ) ;
78+ const [ errors , setErrors ] = useState < string [ ] > ( [ ] ) ;
79+
80+ const clearFileUpload = useCallback ( ( ) => {
81+ setFileUpload ( '' ) ;
82+ setErrors ( [ ] ) ;
83+ } , [ setFileUpload , setErrors ] ) ;
84+
85+ const onFileDrop = useCallback (
86+ ( event : DropEvent , files : File [ ] ) => {
87+ event . preventDefault ( ) ;
88+ clearFileUpload ( ) ;
89+ const fileAcc = [ ] ;
90+
91+ files . forEach ( ( yamlFile : File , i : number ) => {
92+ if ( ! yamlFile ) {
93+ return ;
94+ }
95+ const lastFile = i === files . length - 1 ;
96+ const reader = new FileReader ( ) ;
97+ reader . onload = ( ev ) => {
98+ const arrayBuffer = ev . target . result as ArrayBuffer ;
99+ const buffer = Buffer . from ( new Uint8Array ( arrayBuffer ) ) ;
100+
101+ if ( isText ( null , buffer ) ) {
102+ fileAcc . push ( buffer . toString ( ) . trim ( ) ) ;
103+
104+ if ( ! lastFile ) {
105+ fileAcc . push ( '---' ) ;
106+ } else {
107+ setFileUpload ( fileAcc . join ( '\n' ) ) ;
108+ }
109+ }
110+ } ;
111+ reader . readAsArrayBuffer ( yamlFile ) ;
112+ } ) ;
113+ } ,
114+ [ clearFileUpload ] ,
115+ ) ;
116+
117+ const onDropRejected : MultipleFileUploadProps [ 'dropzoneProps' ] [ 'onDropRejected' ] = useCallback (
118+ ( rejections ) => {
119+ setErrors (
120+ rejections . map ( ( rejection ) => {
121+ return getDropErrorMessage (
122+ rejection . errors [ 0 ] . code as DropzoneErrorCode ,
123+ rejection . file . name ,
124+ ) ;
125+ } ) ,
126+ ) ;
127+ } ,
128+ [ getDropErrorMessage , maxFileUploadSize ] ,
129+ ) ;
130+
131+ return (
132+ < MultipleFileUpload
133+ dropzoneProps = { {
134+ maxFiles : allowMultiple ? undefined : 1 ,
135+ maxSize : maxFileUploadSize ,
136+ onDrop : ( acceptedFiles , fileRejections , event ) => {
137+ clearFileUpload ( ) ;
138+
139+ if ( fileRejections . length > 0 ) {
140+ onDropRejected ( fileRejections , event ) ;
141+ }
142+ if ( acceptedFiles . length > 0 ) {
143+ onFileDrop ( event , acceptedFiles as File [ ] ) ;
144+ }
145+ } ,
146+ accept : {
147+ 'application/yaml' : [ '.yaml' , '.yml' ] ,
148+ } ,
149+ } }
150+ style = { { display : 'contents' } }
151+ >
152+ < div className = "co-file-dropzone co-file-dropzone__flex" >
153+ < div className = "co-file-dropzone-container" >
154+ < p className = "co-file-dropzone__drop-text" > { t ( 'Drop file here' ) } </ p >
155+ </ div >
156+ < EditYAML
157+ { ...props }
158+ allowMultiple = { allowMultiple }
159+ obj = { initialResource }
160+ fileUpload = { fileUpload }
161+ error = { errors . join ( '\n' ) }
162+ clearFileUpload = { clearFileUpload }
163+ create = { create }
164+ onChange = { onChange }
165+ hideHeader = { hideHeader }
166+ isCodeImportRedirect = { isCodeImportRedirect }
167+ />
168+ </ div >
169+ </ MultipleFileUpload >
170+ ) ;
171+ } ;
172+
37173// Prevents SDK users from passing additional props
38- export const ResourceYAMLEditor : React . FC < ResourceYAMLEditorProps > = ( {
174+ export const ResourceYAMLEditor : React . FCC < ResourceYAMLEditorProps > = ( {
39175 initialResource,
40176 header,
41177 onSave,
@@ -54,127 +190,3 @@ export const ResourceYAMLEditor: React.FC<ResourceYAMLEditorProps> = ({
54190 hideHeader = { hideHeader }
55191 />
56192) ;
57-
58- export const DroppableEditYAML = withDragDropContext < DroppableEditYAMLProps > (
59- class DroppableEditYAML extends React . Component < DroppableEditYAMLProps , DroppableEditYAMLState > {
60- private fileUploadContents : string = '' ;
61- constructor ( props ) {
62- super ( props ) ;
63- this . state = {
64- fileUpload : '' ,
65- errors : [ ] ,
66- } ;
67- this . handleFileDrop = this . handleFileDrop . bind ( this ) ;
68- this . clearFileUpload = this . clearFileUpload . bind ( this ) ;
69- }
70-
71- addDocument ( newFileContent : string ) {
72- this . fileUploadContents = _ . isEmpty ( this . fileUploadContents )
73- ? newFileContent
74- : `${ this . fileUploadContents } \n---\n${ newFileContent } ` ;
75- }
76-
77- readFileContents ( file , lastFile ) {
78- // If unsupported file type is dropped into drop zone, file will be undefined
79- if ( ! file ) {
80- return ;
81- }
82- // limit size size uploading to 1 mb
83- if ( file . size <= maxFileUploadSize ) {
84- const reader = new FileReader ( ) ;
85- reader . onload = ( ) => {
86- // The type is actually an ArrayBuffer (as we use readAsArrayBuffer), but TS doesn't know that
87- const buffer = Buffer . from ( reader . result as ArrayBuffer ) ;
88- if ( ITOB . isBinary ( null , buffer ) ) {
89- this . setState ( ( previousState ) => ( {
90- errors : [ ...previousState . errors , `Ignoring ${ file . name } : ${ fileTypeErrorMsg } ` ] ,
91- } ) ) ;
92- } else {
93- this . addDocument ( buffer . toString ( ) . trim ( ) ) ;
94- if ( lastFile ) {
95- this . setState ( { fileUpload : this . fileUploadContents } ) ;
96- }
97- }
98- } ;
99- reader . readAsArrayBuffer ( file ) ;
100- } else {
101- this . setState ( ( previousState ) => ( {
102- errors : [ ...previousState . errors , `Ignoring ${ file . name } : ${ fileSizeErrorMsg } ` ] ,
103- } ) ) ;
104- }
105- }
106-
107- handleFileDrop ( item , monitor ) {
108- const { allowMultiple } = this . props ;
109- if ( ! monitor ) {
110- return ;
111- }
112- this . clearFileUpload ( ) ;
113- if ( allowMultiple ) {
114- monitor . getItem ( ) . files . forEach ( ( yamlFile , i ) => {
115- this . readFileContents ( yamlFile , i === monitor . getItem ( ) . files . length - 1 ) ;
116- } ) ;
117- } else {
118- const [ file ] = monitor . getItem ( ) . files ;
119- this . readFileContents ( file , true ) ;
120- }
121- }
122-
123- clearFileUpload ( ) {
124- this . setState ( { fileUpload : '' , errors : [ ] } ) ;
125- this . fileUploadContents = '' ;
126- }
127-
128- render ( ) {
129- const {
130- allowMultiple,
131- initialResource,
132- create = false ,
133- onChange = ( ) => null ,
134- hideHeader = false ,
135- isCodeImportRedirect = false ,
136- } = this . props ;
137- const { errors, fileUpload } = this . state ;
138- return (
139- < EditYAMLComponent
140- { ...this . props }
141- allowMultiple = { allowMultiple }
142- obj = { initialResource }
143- fileUpload = { fileUpload }
144- error = { errors . join ( '\n' ) }
145- onDrop = { this . handleFileDrop }
146- clearFileUpload = { this . clearFileUpload }
147- create = { create }
148- onChange = { onChange }
149- hideHeader = { hideHeader }
150- isCodeImportRedirect = { isCodeImportRedirect }
151- />
152- ) ;
153- }
154- } ,
155- ) ;
156-
157- type EditYAMLProps = {
158- allowMultiple ?: boolean ;
159- obj : ResourceYAMLEditorProps [ 'initialResource' ] ;
160- fileUpload : string ;
161- error : string ;
162- onDrop : ( item : any , monitor : DropTargetMonitor ) => void ;
163- clearFileUpload : ( ) => void ;
164- create ?: boolean ;
165- onChange ?: ( content : string ) => void ;
166- hideHeader ?: boolean ;
167- isCodeImportRedirect ?: boolean ;
168- } ;
169-
170- export type DroppedFile = {
171- error ?: string ;
172- id : string ;
173- name : string ;
174- size : number ;
175- } ;
176-
177- export type DroppableEditYAMLState = {
178- errors : string [ ] ;
179- fileUpload : string ;
180- } ;
0 commit comments