componente · @solu30/ui-kit/input
ImageUpload
Subida de imágenes con grilla de miniaturas y borrado, agnóstica de dónde se guarden los archivos.
Seguir leyendo — 2 párrafos más
Storage-agnostic image upload widget.
Renders a dropzone / thumbnail grid + optional gallery-picker trigger. All storage concerns (tRPC, Cloudflare, S3…) live in the onUpload prop.
4 sitios de import en 3 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- gestorsalon
- luigi
- raquetea
El componente, montado
Cargando el espécimen…
export function ImageUploadSpecimen() {
const [imagenes, setImagenes] = useState<ImageItem[]>([
{ id: 'salon-terraza', url: MINIATURA('Salón Terraza', '#2f5d50'), alt: 'Salón Terraza' },
{ id: 'salon-jardin', url: MINIATURA('Salón Jardín', '#4a4066'), alt: 'Salón Jardín' },
])
return (
<Stage label="ImageUpload · dropzone + miniaturas, con un onUpload local que no toca ningún backend" tall wide>
<div className="w-full max-w-[560px]">
<ImageUpload
images={imagenes}
maxImages={6}
onUpload={async (file) => {
const item: ImageItem = { id: `${file.name}-${file.size}`, url: URL.createObjectURL(file), alt: file.name }
setImagenes((prev) => [...prev, item])
return item
}}
onDelete={async (id) => {
setImagenes((prev) => prev.filter((i) => i.id !== id))
}}
/>
</div>
</Stage>
)
}De specimens/input.tsx · ImageUploadSpecimen: el espécimen del Muestrario, no un ejemplo oficial del paquete. Muestra cómo se usa la pieza de verdad, montada contra los tipos instalados —eso es lo que vale— pero no es documentación del kit.
Cómo se usa
import { ImageUpload } from '@solu30/ui-kit/input'Props
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| images | sí | ImageItem[] | Currently persisted images |
| onUpload | sí | (file: File) => Promise<ImageItem> | Called when the user drops / selects a file. Must upload the file and return the resulting { id, url }. |
| onDelete | opcional | (id: string) => Promise<void> | Called when the user clicks the delete button on a thumbnail |
| onReorder | opcional | (ids: string[]) => void | @deprecated NO HACE NADA. El reordenamiento por arrastre nunca se implementó: no hay `draggable`, ni handlers de drag, ni nada que pueda invocar este callback — se recibe y se descarta. Hasta 5.11.0 la grilla además dibujaba un asa de arrastre (`GripVertical`) al pasar el mouse, que prometía en pantalla lo que el componente no hacía; se quitó junto con esta deprecación. Si necesitás reordenar, ordená `images` desde el host y volvé a pasarlo. Se elimina en el próximo major. |
| maxImages | opcional | number | Maximum number of images allowed (default 10) |
| accept | opcional | string | Accepted MIME types joined as accept string (default 'image/*') |
| multiple | opcional | boolean | Allow selecting multiple files at once (default true) |
| compact | opcional | boolean | Compact row mode: thumbnails + inline add button instead of full dropzone |
| showGalleryButton | opcional | boolean | When true, renders an "Open gallery" button below the dropzone. The button just fires onGalleryOpen — the host decides what dialog to show. |
| onGalleryOpen | opcional | () => void | — |
| className | opcional | string | — |
Ejemplo del paquete
<ImageUpload
images={images}
onUpload={async (file) => {
const result = await trpc.images.upload.mutateAsync(file)
return { id: result.id, url: result.url }
}}
onDelete={async (id) => { await trpc.images.delete.mutateAsync({ id }) }}
maxImages={6}
/>Del @example del JSDoc de @solu30/ui-kit, sin editar: son 65 de los 541 símbolos. La sangría y los renglones en blanco son los del paquete.