92 lines
2.7 KiB
TypeScript
92 lines
2.7 KiB
TypeScript
import React, { useRef } from 'react'
|
|
import { cn } from '../lib/utils'
|
|
import { Plus, X } from 'lucide-react'
|
|
import type { ImageUploaderNeoItem, ImageUploaderNeoPayload, ImageUploaderNeoProps } from '@packages/shared'
|
|
import { div } from './my-touchable-opacity'
|
|
|
|
export type { ImageUploaderNeoItem, ImageUploaderNeoPayload } from '@packages/shared'
|
|
|
|
function usePickImage({
|
|
multiple,
|
|
}: {
|
|
multiple: boolean
|
|
}): () => void {
|
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
|
|
return () => {
|
|
if (!inputRef.current) {
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = 'image/*'
|
|
input.multiple = multiple
|
|
input.onchange = () => {
|
|
const files = Array.from(input.files || [])
|
|
// We just trigger the native picker; actual handling is in the parent
|
|
}
|
|
inputRef.current = input
|
|
}
|
|
inputRef.current.click()
|
|
}
|
|
}
|
|
|
|
export function ImageUploaderNeo({
|
|
images,
|
|
onImageAdd,
|
|
onImageRemove,
|
|
allowMultiple = true,
|
|
}: ImageUploaderNeoProps) {
|
|
const totalCount = images.length
|
|
|
|
const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const files = Array.from(e.target.files || [])
|
|
if (files.length === 0) return
|
|
|
|
const payloads: ImageUploaderNeoPayload[] = files.map((file) => ({
|
|
url: URL.createObjectURL(file),
|
|
mimeType: file.type,
|
|
}))
|
|
|
|
onImageAdd(payloads)
|
|
// Reset input so same file can be picked again
|
|
e.target.value = ''
|
|
}
|
|
|
|
return (
|
|
<div className="mb-4">
|
|
<div className="flex flex-wrap gap-2">
|
|
{images.map((image, index) => (
|
|
<div key={index} className="relative w-1/3 aspect-square">
|
|
<img
|
|
src={image.imgUrl}
|
|
alt={`Upload ${index + 1}`}
|
|
className="h-full w-full rounded object-cover"
|
|
/>
|
|
<div
|
|
onClick={() =>
|
|
onImageRemove({
|
|
url: image.imgUrl,
|
|
mimeType: image.mimeType ?? null,
|
|
})
|
|
}
|
|
className="absolute right-1 top-1 rounded-full bg-red-500 p-1 text-white"
|
|
>
|
|
<X className="h-3 w-3" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
{(!allowMultiple || totalCount < 1) && (
|
|
<label className="flex aspect-square w-1/3 cursor-pointer items-center justify-center rounded bg-gray-200 opacity-75 hover:opacity-100">
|
|
<Plus className="h-8 w-8 text-gray-600" />
|
|
<input
|
|
type="file"
|
|
accept="image/*"
|
|
multiple={allowMultiple}
|
|
onChange={handleFileInput}
|
|
className="hidden"
|
|
/>
|
|
</label>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|