Formularios
Estado: BetaZona para subir archivos
Para las fotos de un producto, la portada de un evento, el slide del inicio o la foto de perfil. La zona reacciona con resorte al arrastrar encima, cada archivo aparece con miniatura, nombre, tamaño y botón para quitarlo, y lo que no cumple (formato, peso, cantidad) se explica donde Field pone sus errores. El input de archivo real se mantiene sincronizado, así que el formulario lo manda como siempre; mientras se envía dice "Subiendo…".
- Arrastre
- Resorte
- Formularios
- Turbo
Arrastra tus fotos aquí
o elige desde tu equipo
PNG, JPG o WEBP · hasta 5 MB · máx. 6
-
tapete-arena-portada.jpg Ya adjunta
Lista para subir
La primera es la portada en la tienda.
Sube una foto
PNG, JPG o WEBP · hasta 5 MB
Arrastra tus fotos aquí
o elige desde tu equipo
PNG, JPG o WEBP · hasta 5 MB · máx. 6
-
tapete-arena-portada.jpg Ya adjunta
Lista para subir
La primera es la portada en la tienda.
Sube una foto
PNG, JPG o WEBP · hasta 5 MB
app/views/… (la demo de arriba)
<%# Fotos de un producto de la tienda y (en el escenario grande) la foto de perfil de una maestra. %>
<div class="@container w-full">
<div class="mx-auto grid w-full max-w-sm gap-6">
<%= render Ui::DropzoneComponent.new(label: "Fotos del producto", name: "product[images][]", multiple: true,
accept: "image/png,image/jpeg,image/webp", max_size: 5.megabytes, max_files: 6,
attached: [ { url: "/icon.svg", name: "tapete-arena-portada.jpg" } ],
hint: "La primera es la portada en la tienda.") %>
<div class="hidden @lg:block">
<%= render Ui::DropzoneComponent.new(label: "Foto de perfil", name: "user[photo]", mode: :avatar,
accept: "image/png,image/jpeg,image/webp", max_size: 5.megabytes, placeholder: "AR") %>
</div>
</div>
</div>Qué hace
- Arrastrar encima: la zona crece apenas y el ícono sube con resorte; soltar agrega.
- Miniaturas con URL.createObjectURL, liberadas al quitar el archivo o al salir de la página.
- El input real se sincroniza con DataTransfer; sin direct uploads, se manda con el formulario.
- Turbo: con turbo:submit-start dice "Subiendo…" y una barra recorre cada archivo.
- En un solo archivo, lo actual queda atenuado con "Se reemplaza al guardar".
Props
| Nombre | Tipo | Default | Descripción |
|---|---|---|---|
| label | String | — | Etiqueta (Ui::FieldComponent), enlazada al input de archivo. |
| name | String | nil | Nombre del input. Con form + attribute sale del form builder (con [] si es múltiple). |
| form / attribute | FormBuilder / Symbol | nil | Toma name, id y error del modelo. |
| multiple | Boolean | false | Varios archivos (has_many_attached). |
| accept | String | Array | nil | Tipos permitidos ("image/*", "image/png,image/jpeg", ".pdf"); también se validan en el cliente. |
| max_size | Integer (bytes) | nil | Peso máximo por archivo (5.megabytes). |
| max_files | Integer | nil | Cuántos archivos nuevos como máximo (solo con multiple). |
| attached | [{ url:, name:, remove_url: }] | [] | Lo que ya está adjunto (current: es su nombre anterior). Con remove_url aparece un botón de eliminar (turbo_method: :delete). |
| mode | :files | :avatar | :files | :avatar es compacta y circular, para la foto de perfil (variant: es su nombre anterior). |
| title / placeholder | String | según accept | Texto de la zona; iniciales del avatar sin foto. |
| hint / error / required / hide_label | — | — | Los de Ui::FieldComponent. |
Accesibilidad
- El input de archivo es el control enfocable (con su etiqueta, ayuda y error); el anillo de foco se pinta en la zona.
- Clic, Enter o Espacio abren el selector.
- Cada botón de quitar dice qué archivo quita; al quitar, el foco pasa al siguiente.
- Lo que se agrega, se quita o se rechaza se anuncia en una región aria-live.