@@ -45,6 +45,9 @@ export default function ImageOptimizeForm({
4545 aspectRatio : number ;
4646 } [ ]
4747 > ( [ ] ) ;
48+ const [ originalImageNames , setLocalOriginalImageNames ] = useState < string [ ] > (
49+ [ ] ,
50+ ) ;
4851 const [ isQualityEnabled , setIsQualityEnabled ] = useState ( true ) ;
4952 const [ isResizeEnabled , setIsResizeEnabled ] = useState ( true ) ;
5053 const [ outputFormat , setOutputFormat ] = useState < string > ( "webp" ) ;
@@ -60,6 +63,7 @@ export default function ImageOptimizeForm({
6063 format : string ;
6164 aspectRatio : number ;
6265 } [ ] = [ ] ;
66+ const originalNamesTemp : string [ ] = [ ] ;
6367
6468 files . forEach ( ( file , index ) => {
6569 const reader = new FileReader ( ) ;
@@ -77,11 +81,13 @@ export default function ImageOptimizeForm({
7781 } ;
7882 previewsTemp [ index ] = reader . result as string ;
7983 imageInfosTemp [ index ] = imageInfoData ;
84+ originalNamesTemp [ index ] = file . name ;
8085
8186 // Only update state when all files are processed
8287 if ( previewsTemp . length === files . length ) {
8388 setPreviews ( [ ...previewsTemp ] ) ;
8489 setLocalImageInfos ( [ ...imageInfosTemp ] ) ;
90+ setLocalOriginalImageNames ( [ ...originalNamesTemp ] ) ;
8591 }
8692 } ;
8793 } ;
@@ -90,6 +96,7 @@ export default function ImageOptimizeForm({
9096 } else {
9197 setPreviews ( [ ] ) ;
9298 setLocalImageInfos ( [ ] ) ;
99+ setLocalOriginalImageNames ( [ ] ) ;
93100 }
94101 } , [ files ] ) ;
95102
@@ -106,6 +113,47 @@ export default function ImageOptimizeForm({
106113 }
107114 } ;
108115
116+ const handleClone = ( ) => {
117+ if ( selectedImageIndex < 0 || selectedImageIndex >= files . length ) return ;
118+
119+ const originalFile = files [ selectedImageIndex ] ! ;
120+ const clonedFile = new File (
121+ [ originalFile ] ,
122+ generateCloneName ( originalFile . name ) ,
123+ {
124+ type : originalFile . type ,
125+ lastModified : originalFile . lastModified ,
126+ } ,
127+ ) ;
128+
129+ const updatedFiles = [ ...files , clonedFile ] ;
130+ setFiles ( updatedFiles ) ;
131+
132+ const newPreviewUrl = previews [ selectedImageIndex ] ! ;
133+ const newImageInfo = { ...imageInfos [ selectedImageIndex ] ! } ;
134+ const newOriginalName = clonedFile . name ;
135+
136+ setPreviews ( [ ...previews , newPreviewUrl ] ) ;
137+ setLocalImageInfos ( [ ...imageInfos , newImageInfo ] ) ;
138+ setLocalOriginalImageNames ( [ ...originalImageNames , newOriginalName ] ) ;
139+
140+ setSelectedImageIndex ( updatedFiles . length - 1 ) ; // Select the cloned image
141+ } ;
142+
143+ const generateCloneName = ( originalName : string ) => {
144+ const extension = originalName . slice ( originalName . lastIndexOf ( "." ) ) ;
145+ const baseName = originalName . slice ( 0 , originalName . lastIndexOf ( "." ) ) ;
146+ let cloneCount = 1 ;
147+
148+ let newName = `${ baseName } -${ cloneCount } ${ extension } ` ;
149+ while ( originalImageNames . includes ( newName ) ) {
150+ cloneCount ++ ;
151+ newName = `${ baseName } -${ cloneCount } ${ extension } ` ;
152+ }
153+
154+ return newName ;
155+ } ;
156+
109157 const handleSubmit = async ( e : React . FormEvent ) => {
110158 e . preventDefault ( ) ;
111159 if ( files . length === 0 ) return ;
@@ -216,6 +264,13 @@ export default function ImageOptimizeForm({
216264 </ option >
217265 ) ) }
218266 </ select >
267+ < button
268+ type = "button"
269+ className = "mt-2 w-full rounded bg-yellow-500 py-2 text-white hover:bg-yellow-600"
270+ onClick = { handleClone }
271+ >
272+ Clone
273+ </ button >
219274 </ div >
220275 ) }
221276
0 commit comments