Skip to content

Instantly share code, notes, and snippets.

@drewminns
Created June 17, 2020 19:17
Show Gist options
  • Save drewminns/9fa0a1f2c7d2ca6e41bb37af285a4ac3 to your computer and use it in GitHub Desktop.
Save drewminns/9fa0a1f2c7d2ca6e41bb37af285a4ac3 to your computer and use it in GitHub Desktop.
import React, { RefForwardingComponent, useReducer, useState, useEffect, forwardRef, useImperativeHandle } from 'react'
import ImageKit from 'imagekit-javascript'
import { ImageKitResponse } from '../interfaces'
const imagekit = new ImageKit({
publicKey: process.env.IMAGEKIT_PUBLIC_KEY || 'fasdf',
urlEndpoint: process.env.IMAGEKIT_ENDPOINT || 'asdfasdf',
authenticationEndpoint: '/api/auth/assets',
})
export interface ImageUploadRefMethods {
uploadImageKitService: () => Promise<ImageKitResponse[]>
}
interface ImageUploadProps {
onUpdate: (response: File[]) => any
ref: any
}
const initialState: File[] = []
const imageListReducer = (state: File[], action: { type: string; payload?: any }) => {
switch (action.type) {
case 'add':
const data = [...state, ...action.payload]
return Array.from(new Set(data))
case 'remove':
return state.filter((file: File) => file.name !== action.payload)
case 'clear':
return initialState
default:
throw new Error()
}
}
const ImageUploadComponent: React.RefForwardingComponent<ImageUploadRefMethods, ImageUploadProps> = (
{ onUpdate },
ref,
) => {
const [fileList, dispatch] = useReducer(imageListReducer, initialState)
const [isDraggedOver, updatedDragStatus] = useState<boolean>(false)
useEffect(() => {
onUpdate(fileList)
}, [fileList])
useImperativeHandle(ref, () => ({
async uploadImageKitService(): Promise<ImageKitResponse[]> {
try {
const uploadGroup = fileList.map(
(file: File) =>
new Promise((resolve) =>
imagekit.upload({ file, fileName: file.name }, (err: any, result: ImageKitResponse) => resolve(result)),
),
)
const result = await Promise.all(uploadGroup)
dispatch({ type: 'clear' })
return result as ImageKitResponse[]
} catch (err) {
throw new Error()
}
},
}))
function addFilesOnDrop(evt: React.DragEvent<HTMLDivElement>): void {
evt.preventDefault()
evt.stopPropagation()
updatedDragStatus(false)
dispatch({ type: 'add', payload: evt.dataTransfer.files })
onUpdate(fileList)
}
function addFilesInput(evt: React.ChangeEvent<HTMLInputElement>): void {
evt.preventDefault()
evt.stopPropagation()
dispatch({ type: 'add', payload: evt.target.files })
onUpdate(fileList)
}
function trackDrag(event: Event | React.DragEvent<HTMLDivElement>, status: boolean): void {
event.stopPropagation()
event.preventDefault()
updatedDragStatus(status)
}
return (
<div>
<div
style={{ height: '300px', borderWidth: isDraggedOver ? '2px' : '1px', borderColor: '#000' }}
onDrop={addFilesOnDrop}
onDragOver={(e) => trackDrag(e, true)}
onDragLeave={(e) => trackDrag(e, false)}
>
<p>
<label htmlFor="file">Choose Images</label> or drop them here
</p>
<ul style={{ display: 'flex' }}>
{fileList.map((file: File) => (
<li key={file.name + file.size} style={{ width: '150px', position: 'relative' }}>
<img style={{ maxWidth: '100%' }} src={URL.createObjectURL(file)} alt={file.name} />
<button onClick={() => dispatch({ type: 'remove', payload: file.name })}>Remove</button>
</li>
))}
</ul>
</div>
<input type="file" style={{ display: 'none' }} multiple id="file" onChange={addFilesInput} />
</div>
)
}
ImageUploadComponent.displayName = 'Image Upload'
export const ImageUpload = forwardRef(ImageUploadComponent)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment