Saltar al contenido
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
Primeros pasosWebReactSwiftUIJetpack ComposeFlutterReact NativePreajustesTu propio sistema de diseñoUsar Tanrei con agentes de IAMarcaTokens de diseñoSistemas de diseño y personalizaciónTheme StudioColorTipografíaIconosFormaElevaciónMovimientoEstados de interacciónComportamientoDiseño de páginaAccesibilidadTextosTokens de colorTokens de la escala tipográficaTokens de formaTokens de elevaciónTokens de movimientoTokens de estadoTokens de espacio y de medidaComponentesBotónBotón de acción principalBotón segmentadoGrupo de botonesBotón con menúBarra de herramientasBotón de copiarCasilla de verificaciónBotón de opciónInterruptorChipMenúControl deslizanteCampo de control deslizanteCalendarioDateTimePickerGrupo de camposGrupo de botones de opciónGrupo de casillas de verificaciónSelectCampo de selecciónBarra de menúsSubida de archivosSelector de archivos AvanzadoZona de soltar archivosEntrada de texto AvanzadoCampo de textoCampo de chipsCampoCuadro combinado AvanzadoCampo de cuadro combinadoEntrada numérica AvanzadoCampo numéricoEntrada de color AvanzadoCampo de colorEntrada de código de un solo uso AvanzadoCampo de código de un solo usoEntrada de texto incrustada AvanzadoCuadro de redacción de chatTarjetaDivisorCapa modal AvanzadoDiálogoListaFila de listaHoja inferiorHoja lateralCarruselBloque de códigoTablaPopoverPasosAcordeónLista de descripcionesFiguraEstructura de la aplicaciónCabecera de páginaVista de árbolLínea de tiempoVista previa de imágenesTirador de tamañoMarco redimensionableÁrea de desplazamientoInsigniaIndicador de progresoMedidorContador animadoIndicador de cargaSnackbarTooltipAtajo de tecladoTagCódigo en líneaCalloutAvatarEstado vacíoGrupo de tagsEsqueletoElemento de archivoMuestraTexto en marquesinaPestañasVista de pestañasBarra de la aplicaciónBarra de navegaciónPanel de navegaciónBúsquedaEnlacePaginaciónEnlaces anterior y siguienteIndicador de pasosRuta de navegaciónTabla de contenidoTarjeta de enlaceMenú FABEstadoCómo se construye TanreiCómo contribuirLicencia y atribución
InicioComponentesSubida de archivos

Subida de archivos

Permite elegir archivos con un selector de archivos y muestra lo elegido como elementos de archivo, en una columna de filas o en una tira de mosaicos. Colocado en un formulario, conserva los archivos elegidos y los envía sin código; un producto que envía los archivos por su cuenta da los elementos de archivo. Nunca sube nada por sí mismo.

content_copyVer en Markdown
Categoría
Selección
Tokens
comp.file-upload
Nivel
Funcionalidad
Se compone de
File picker, File item

Descripción general

Web
tanrei-file-upload
schedule
SwiftUI
TanreiFileUpload
radio_button_unchecked
Jetpack Compose
TanreiFileUpload
radio_button_unchecked
Flutter
TanreiFileUpload
radio_button_unchecked
React Native
FileUpload
radio_button_unchecked

Generado a partir de docs/status/status.json, specs/file-upload.spec.json

Un control de subida de archivos en cada disposición. Elige un archivo: aparece en la lista y se puede quitar. No se envía nada.

Elegir un archivoElegir archivos

Un control de subida de archivos es dos cosas en un mismo lugar: la forma de elegir archivos y la lista de lo que se eligió. Es el caso corriente, listo para usarse en un formulario.

Se compone de un selector de archivos y elementos de archivo. El selector de archivos es la forma de elegir: tu propio botón, que abre el selector de archivos de la plataforma, la zona para soltar que lo rodea, los límites dichos con palabras y el juicio de lo elegido. Un elemento de archivo es un archivo elegido: su glifo o su imagen, su nombre, su tamaño, su estado y las acciones para quitarlo y para volver a intentarlo. El control de subida no dibuja ni lo uno ni lo otro. Añade lo que los une: qué archivos se conservan, cuántos archivos más caben, la misma forma (fila o mosaico) para todos los archivos, un solo mensaje para muchos cambios, adónde va el foco después de quitar un archivo y el valor del formulario.

Quién tiene las filas lo dice la propiedad rows:

  • own, el valor predeterminado. El control de subida conserva los archivos elegidos y dibuja él mismo sus filas. Colócalo en un formulario y funciona: las personas eligen, ven la lista, quitan y envían. No escribes código.
  • given. Tu producto da las filas. Úsalo cuando envías cada archivo en cuanto se elige y quieres que cada fila muestre su estado, su progreso y una acción de reintentar.

No sube nada

El control de subida no abre ninguna conexión ni lee un solo byte de un archivo. Con rows="own" conserva la referencia del navegador a cada archivo elegido, y el formulario envía los archivos cuando se envía. Con rows="given" no conserva nada. Adónde van los archivos, con qué credenciales, en qué fragmentos y con qué reintentos es cosa del código de tu formulario o de tu producto. Los ejemplos de esta página no envían nada.

Usa otra cosa cuando:

  • Los archivos ya están almacenados y las personas los abren o los seleccionan. Eso es una lista.
  • Las personas introducen valores, como destinatarios o etiquetas. Esos son chips de entrada en un grupo de tags.
  • Solo muestras cuánto ha avanzado una tarea. Eso es un indicador de progreso.
  • Los archivos van a un lugar que no es una lista debajo del selector: dentro de un mensaje que se está escribiendo, a un lienzo, o se leen en el momento y no se conservan. Eso es un selector de archivos solo, con elementos de archivo allí donde los muestres; consulta Un nivel más abajo.
  • La página debe recibir archivos donde no se ejecutan scripts. Escribe un <input type="file"> nativo en tu propio formulario. Un elemento personalizado no puede abrir el selector ni llevar un archivo sin scripts.

En la web es <tanrei-file-upload>. Las demás plataformas todavía no lo han implementado.

Uso

Coloca el elemento en tu formulario y dale un name y un activador. Eso es todo. Los archivos elegidos aparecen en la lista, cada fila tiene una acción de quitar y el formulario envía los archivos que se conservan.

<form method="post" enctype="multipart/form-data" action="/requests">
  <tanrei-file-upload name="attachments" multiple
                      accept=".pdf,image/png" max-size="10000000"
                      label="Archivos adjuntos"
                      constraints-text="PDF o PNG, hasta 10 MB cada uno"
                      remove-label="Quitar {name}"
                      type-error-text="Solo se pueden añadir archivos PDF y PNG."
                      size-error-text="Supera los 10 MB.">
    <tanrei-button slot="trigger" variant="outlined">Elegir archivos</tanrei-button>
  </tanrei-file-upload>
  <button type="submit">Enviar</button>
</form>
import { Button, FileUpload } from '@tanrei/react';

<form method="post" encType="multipart/form-data" action="/requests">
  <FileUpload
    name="attachments"
    multiple
    accept={['.pdf', 'image/png']}
    maxSize={10_000_000}
    label="Archivos adjuntos"
    constraintsText="PDF o PNG, hasta 10 MB cada uno"
    removeLabel="Quitar {name}"
    typeErrorText="Solo se pueden añadir archivos PDF y PNG."
    sizeErrorText="Supera los 10 MB."
  >
    <Button slot="trigger" variant="outlined">Elegir archivos</Button>
  </FileUpload>
  <button type="submit">Enviar</button>
</form>

Qué ocurre sin escribir nada:

  • Con multiple, una segunda elección se suma a la primera. maxFiles cuenta todos los archivos que se conservan. Sin multiple, un archivo nuevo ocupa el lugar del que se conservaba.
  • El mismo archivo elegido dos veces se conserva una sola vez.
  • Un archivo que incumple un límite no se conserva. Se dibuja como una fila fallida con tu frase para el motivo, y desaparece cuando la persona lo quita o vuelve a elegir.
  • La acción de quitar quita la fila. El foco pasa a la fila siguiente.
  • El formulario envía los archivos conservados con el nombre name. Un restablecimiento vacía la lista.

Tres líneas de código opcionales añaden lo que el marcado no puede decir:

const upload = document.querySelector('tanrei-file-upload');

// El tamaño de un archivo, con las palabras de tu producto. Sin esto no se dibuja ningún tamaño.
const megabytes = new Intl.NumberFormat('es', { style: 'unit', unit: 'megabyte', maximumFractionDigits: 1 });
upload.formatSize = (bytes) => megabytes.format(bytes / 1e6);

// Los archivos conservados cambiaron por algo que hizo una persona.
upload.addEventListener('change', ({ detail }) => console.log(detail.files));

// Desde tu código: lee los archivos conservados, entrega archivos y vacía la lista.
upload.files;          // File[], una matriz nueva en cada lectura
upload.add(files);     // se juzgan como una elección; devuelve los archivos rechazados con sus motivos
upload.clear();

En React, esas mismas tres cosas son una ref: ref.current.files, ref.current.add(files) y ref.current.clear(), con formatSize y onChange como propiedades.

Enviar los archivos por tu cuenta

Di rows="given" cuando tu producto envía cada archivo por su cuenta e informa sobre él. En select añades un elemento de archivo por cada archivo que conservas. En el remove de un elemento de archivo, lo retiras. El componente entonces no añade ni quita nada por sí mismo, y no conserva ningún archivo.

<tanrei-file-upload id="attachments" rows="given" layout="drop-area" multiple
                    accept=".pdf,image/png" max-size="10000000"
                    label="Archivos adjuntos"
                    drop-text="Arrastra archivos aquí o elígelos"
                    constraints-text="PDF o PNG, hasta 10 MB cada uno"
                    remove-label="Quitar {name}" retry-label="Reintentar {name}">
  <tanrei-button slot="trigger" variant="outlined">Elegir archivos</tanrei-button>
</tanrei-file-upload>

<script type="module">
  import '@tanrei/web/components/file-upload';
  import '@tanrei/web/components/button';

  const upload = document.querySelector('#attachments');
  const size = new Intl.NumberFormat('es', { style: 'unit', unit: 'megabyte', maximumFractionDigits: 1 });

  upload.addEventListener('select', ({ detail }) => {
    for (const file of detail.files) {
      const row = document.createElement('tanrei-file-item');
      Object.assign(row, { value: crypto.randomUUID(), name: file.name, sizeText: size.format(file.size / 1e6), status: 'uploading', statusText: 'Subiendo', progress: 0 });
      upload.append(row);
      // Tu código envía el archivo e informa del resultado. El componente solo dibuja lo que tú defines.
      send(file, {
        onProgress: (share) => (row.progress = share),
        onDone: () => Object.assign(row, { status: 'done', statusText: 'Subido' }),
        onError: () => Object.assign(row, { status: 'error', errorText: 'Se perdió la conexión.', retryable: true }),
      });
    }
  });

  // Un archivo rechazado tiene un motivo que es un nombre, no una frase. Dilo con tus propias palabras.
  upload.addEventListener('reject', ({ detail }) => {
    const tooLarge = detail.files.filter((refused) => refused.reason === 'size').length;
    if (tooLarge) upload.announcement = `${tooLarge} archivos superaban los 10 MB.`;
  });

  // Una fila nunca se quita a sí misma. La retiras tú, y el foco pasa a la fila siguiente.
  upload.addEventListener('remove', (event) => event.target.remove());
</script>
import { Button, FileUpload, FileItem } from '@tanrei/react';

<FileUpload
  rows="given"
  layout="drop-area"
  multiple
  accept={['.pdf', 'image/png']}
  maxSize={10_000_000}
  label="Archivos adjuntos"
  dropText="Arrastra archivos aquí o elígelos"
  constraintsText="PDF o PNG, hasta 10 MB cada uno"
  removeLabel="Quitar {name}"
  retryLabel="Reintentar {name}"
  onSelect={(event) => start(event.detail.files)}
  onReject={(event) => explain(event.detail.files)}
>
  <Button slot="trigger" variant="outlined">Elegir archivos</Button>
  {rows.map((row) => (
    <FileItem key={row.id} value={row.id} name={row.name} sizeText={row.sizeText} status={row.status} statusText={row.statusText} progress={row.progress} errorText={row.errorText} retryable={row.retryable} onRemove={() => remove(row.id)} onRetry={() => retry(row.id)} />
  ))}
</FileUpload>

Las filas solo se dibujan cuando las das tú

Un <tanrei-file-item> que escribes dentro de un control de subida solo se dibuja con rows="given". Sin el atributo, el control de subida conserva sus propios archivos, tus filas no se dibujan y una compilación de depuración lo notifica.

Un nivel más abajo

Lo mismo, escrito a mano a partir de sus dos miembros, da la misma imagen, con los mismos nombres, descripciones y estados:

<!-- el control de subida de archivos -->
<tanrei-field label="Archivos adjuntos">
  <tanrei-file-upload multiple accept=".pdf" max-files="5" rows="given"
      constraints-text="PDF, 5 archivos como máximo" empty-text="Ningún archivo elegido" remove-label="Quitar {name}" retry-label="Reintentar {name}">
    <tanrei-button slot="trigger" variant="outlined">Elegir archivos</tanrei-button>
    <tanrei-file-item value="a" name="brief.pdf" size-text="820 KB" status="uploading" status-text="Subiendo" progress="0.4"></tanrei-file-item>
  </tanrei-file-upload>
</tanrei-field>

<!-- un nivel más abajo: los dos miembros, dispuestos por el producto -->
<tanrei-field label="Archivos adjuntos">
  <tanrei-file-picker multiple accept=".pdf" max-files="5"
      constraints-text="PDF, 5 archivos como máximo" selection-text="Ningún archivo elegido">
    <tanrei-button slot="trigger" variant="outlined">Elegir archivos</tanrei-button>
  </tanrei-file-picker>
</tanrei-field>
<ul class="my-files" aria-label="Archivos adjuntos">
  <li><tanrei-file-item value="a" name="brief.pdf" size-text="820 KB" status="uploading" status-text="Subiendo" progress="0.4"
        remove-label="Quitar {name}" retry-label="Reintentar {name}"></tanrei-file-item></li>
</ul>

Lo mismo escrito a mano: un selector de archivos y una lista propia de la página con un elemento de archivo

Elegir archivos

La fila de un archivo es el mismo elemento por separado y dentro de un control de subida. Lo que la versión hecha a mano no recibe es el estado que mantiene el control de subida:

  • Un solo mensaje cortés para muchos cambios.
  • El foco que pasa al archivo siguiente después de quitar uno.
  • El valor del formulario, su restablecimiento y su validación.
  • Pasar por alto un archivo que se eligió dos veces, y juzgar cuántos archivos más caben.
  • La misma forma (fila o mosaico), el mismo encaje (cover o contain) y los mismos dos nombres de acción para todos los archivos.
  • La lista que rodea los elementos, y el grupo que rodea el conjunto.

Baja a ese nivel cuando necesites una organización que el control de subida no ofrece:

Necesitas Usa
Una forma de elegir, una lista debajo y el valor de un formulario El control de subida de archivos
Los archivos elegidos en un lugar que no es debajo del selector: dentro de un mensaje, junto a una vista previa, en una tabla Un selector de archivos, y elementos de archivo donde los muestres
Un archivo que se lee en el momento y no se conserva Un selector de archivos solo
Archivos que tu producto ya tiene, sin nada que elegir Elementos de archivo solos

Especificaciones

Filas que da el producto, una de cada estado: en espera, enviada al 40%, en curso, completada, fallida y reintentable, fallida definitivamente

Elegir archivos

El mismo control de subida con mosaicos: cada archivo es un recuadro con su imagen y el nombre debajo

Elegir fotos

Qué aporta cada miembro

Aspecto Del selector de archivos Del elemento de archivo Propio del control de subida
Propiedades layout, multiple, accept, maxSize, constraintsText, dropText, disabled, error, required itemForm, itemPreview, itemFit, itemNameHidden, itemWidth, itemHeight (los form, preview, fit, nameHidden, width, height del elemento), removeLabel, retryLabel rows, maxFiles, label, emptyText, typeErrorText, sizeErrorText, countErrorText, announcement
Eventos — click, remove, retry, de los elementos de archivo que das tú select, reject, change
Slots trigger, icon — El slot predeterminado: tus elementos de archivo, con rows="given"
Tokens comp.file-picker.* comp.file-item.* comp.file-upload.list.gap, comp.list.gap.small

El selector de archivos tiene un label, un maxFiles y un selectionText propios. No se le reenvían: el label del control de subida nombra el selector y la lista juntos, su maxFiles cuenta todos los archivos de la lista, y es él quien escribe el selectionText del selector a partir de emptyText mientras la lista está vacía.

Las ocho propiedades que vienen del elemento de archivo se dan a todos los elementos de archivo del control de subida, para que todos los archivos se dibujen de la misma manera. Si un elemento de archivo tuyo dice otra cosa en alguna de ellas, se impone el valor del control, y una compilación de depuración lo notifica. disabled también se da a todos los elementos de archivo.

Lo que hace la elección —el activador, la zona para soltar, accept y los límites— está en la página del selector de archivos. Lo que muestra la fila o el mosaico de un archivo —el glifo según el formato, la imagen, los estados, un cuerpo pulsable— está en la página del elemento de archivo.

Diferenciar un solo control de subida

El control de subida tiene dos tokens propios, las dos distancias de su lista. Todo lo demás que dibuja lee los tokens del selector de archivos y los del elemento de archivo, allí donde estén definidos. Para cambiar una sola instancia, define en ella el token del miembro:

<tanrei-file-upload label="Archivos adjuntos" style="--tanrei-comp-file-item-row-container-gap: 16px">…</tanrei-file-upload>

Anatomía

Parte Descripción
1 group The whole component: the file picker and, below it, the list. Exposed as one named group; it draws nothing of its own.
2 picker The file picker, its member: the trigger with its texts, or the drop area. Its parts — container, outline, icon, label, supporting-text, selection-text — are the file picker's and are drawn from the file picker's tokens.
3 list · opcional The chosen files, one file item each, in the order they were kept, or in the order the product gives them: a List, which it uses, whose items each hold one file item — a column of rows, or lines of tiles that wrap. The List exposes the list and draws the handles and the motion of a reorderable form; the file upload decides the arrangement and the order.
4 item · opcional One file: a file item, its member, drawn from the file item's tokens. The product's own while rows is given; made by the file upload for a file it keeps or refused while rows is own.

Propiedades

Propiedad Tipo Valor predeterminado Descripción
layout button | drop-area "button" How choosing is offered. button: the trigger alone, with the line of limits below it; for a form among other fields, a narrow place, a phone, a toolbar. drop-area: a bounded place around the trigger that also takes files dragged onto it and opens the picker when pressed; for a screen whose main task is to hand over files, where a pointer is likely. Both open the same picker from the same trigger. — File picker
rows own | given "own" Who holds the rows. own: the file upload keeps the files a person chose and draws a row for each itself — choose, see the list, remove, submit, with nothing written by the product; it shows no progress and no result of a sending, because it sends nothing. given: the product gives the rows and the file upload draws them and keeps no file — for a product that sends files itself and reports each one's status and progress. The product says which; the file upload never guesses it from what the list holds.
itemForm row | tile "row" How the file is shown. row: one line across the width the layout gives — the glyph or a small picture, the name, the second line, the bar, the actions at the end — for a column of files. tile: a box of a width and a height, holding the picture or the glyph, with the texts beneath it and the actions at its corner — for a strip of attached pictures. The product chooses by where the file stands. A design system may add values. — File item
itemPreview boolean false Whether a picture of the file is drawn in the glyph's place, where the file is an image and the product gave an address (previewSrc) or the file itself in the platform's idiom. False draws the glyph, also for an image. The picture is drawn by the platform; the file item reads no file. — File item
itemFit cover | contain "cover" How the picture fills its box. cover: it fills the box and what does not fit is cut at the edges, for photographs in a strip. contain: the whole picture is drawn inside the box, on the box's ground, for a picture whose edges matter — a scan, a signature, a diagram. — File item
itemNameHidden boolean false In the tile form: the name is not drawn beneath a tile that draws a picture, for a dense strip of pictures. The name stays the file item's name for assistive technology, and is drawn all the same wherever no picture is (FIT-8). Not read in the row form. — File item
itemWidth number null In the tile form: the width of the box, in the platform's layout unit. Null makes it as wide as it is high. — File item
itemHeight number null In the tile form: the height of the box, in the platform's layout unit. Null reads comp.file-item.tile.media.height. — File item
reorderable boolean false Lets people put the files in another order (FUP-59): the List that arranges the rows gives every row a handle, lets the whole row be dragged, and offers the moves in a menu and to the keyboard. While rows is own the file upload reorders the files it keeps itself and emits change; while rows is given it emits move and the product reorders its file items. Ignored while disabled.
multiple boolean false Whether more than one file can be chosen at a time. False: the picker offers one file, and a drop of several is refused whole. True: the picker offers several. The file picker keeps nothing, so whether a second choice adds to the first or takes its place is decided by whoever keeps the files. — File picker
accept strings The kinds of file that are taken: file name extensions with their dot (".pdf") and media types, a whole family as "image/*". The picker is asked to offer these, and a file of another kind — dropped, or chosen in a picker that did not filter — is refused (reject, reason type). Empty takes every kind. It enforces the limit; constraintsText says it. — File picker
maxSize number null The largest size of one file, in bytes. A larger file is refused (reject, reason size). Null sets no limit. It is judged from the size the platform reports; the file is not read. — File picker
maxFiles number null The most files while multiple is true: the files the file upload keeps while rows is own, the rows the list holds while rows is given. Files chosen beyond the room that is left are refused (reject, reason count). Null sets no limit; ignored while multiple is false. It is the file upload's own and is not given to the file picker, which counts within one choice only: the room is measured against what the file upload holds.
label string "" The accessible name of the group ("Attachments"): the file picker and the list together. It is not drawn. Leave it empty inside a Field, whose label names the group and is drawn; give it where the file upload stands without one. It is the file upload's own: the file picker inside it is given no name and adds no group.
constraintsText string "" The limits, in the product's words, shown before the choice ("PDF or PNG, up to 10 MB each, 5 files at most"). Drawn in the area, or below the trigger, and given to assistive technology as the trigger's description. Tanrei builds no such sentence from accept, maxSize and maxFiles: it has no words. — File picker
dropText string "" The drop area's prompt, in the product's words ("Drag files here, or choose them"). Drawn above the trigger in the drop-area layout; ignored in the button layout. — File picker
emptyText string "" What is shown beside the trigger while no file is chosen, in the button layout ("No file chosen"), and read with the trigger. Empty reads the product's words; with neither, nothing is drawn. The file upload knows whether a file is chosen and gives this text to its file picker as selectionText while the list holds no row, and nothing while it holds one.
removeLabel string "" The accessible name of the remove action: the whole name in the product's language with one place, {name}, which the file item fills with the file's name ("Remove {name}", "{name}を削除"), so that the actions of two files are told apart by their names and the product still writes one word, not one per file. Empty reads the product's words. — File item
retryLabel string "" The accessible name of the retry action, with the place {name} filled by the file's name as removeLabel is ("Retry {name}", "{name}を再試行"). Empty reads the product's words. — File item
dragHandleLabel string "" The accessible name of an item's handle: the whole name in the product's language with one place, {label}, which the list fills with the item's name ("Reorder {label}", "{label}を並べ替え"), so that the handles of two items are told apart. The product's: Tanrei has no word for it. Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
dragHandleHint string "" The description of every handle: how to move an item from it with the keyboard, in the product's words ("Press Space to lift, the arrow keys to move, Space to drop, Escape to cancel; Enter opens the moves"). Empty reads the product's words; with neither, the handle has no description. Given to the List that arranges the rows (FUP-59).
moveToStartLabel string "" The move menu's item that moves the item to the first place ("Move to top", "Move to start"). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
moveBeforeLabel string "" The move menu's item that moves the item one place toward the start ("Move up" in a column, "Move left" in a line). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
moveAfterLabel string "" The move menu's item that moves the item one place toward the end ("Move down", "Move right"). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
moveToEndLabel string "" The move menu's item that moves the item to the last place ("Move to bottom", "Move to end"). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
liftedText string "" The sentence assistive technology is told when the keyboard lifts an item, with three places the list fills: {label} with the item's name, {position} with its place, counted from 1, and {count} with the number of items ("{label} lifted: {position} of {count}"). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
movedText string "" The sentence assistive technology is told when a lifted item reaches another place, and once a move has taken effect, whether it came from the menu, the keyboard or a pointer: {label}, {position} (the place it has, or would have if dropped now, counted from 1) and {count} ("{label}: {position} of {count}"). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
cancelledText string "" The sentence assistive technology is told when a move with the keyboard is cancelled and the item goes back: {label}, {position} (the place it has again) and {count} ("{label} returned to {position} of {count}"). Empty reads the product's words. Required while reorderable. Given to the List that arranges the rows (FUP-59).
typeErrorText string "" While rows is own: the sentence drawn on the row of a file that was refused with the reason type — its kind is not among accept, or it is a folder — in the product's words ("This kind of file cannot be added."). Empty reads the product's words; with neither, the row is drawn without a sentence and that is reported. Not read while rows is given: there the product writes each row's errorText.
sizeErrorText string "" While rows is own: the sentence drawn on the row of a file that was refused with the reason size — it is larger than maxSize — in the product's words ("This file is too large."). Empty reads the product's words; with neither, the row is drawn without a sentence and that is reported. Not read while rows is given: there the product writes each row's errorText.
countErrorText string "" While rows is own: the sentence drawn on the row of a file that was refused with the reason count — more files were chosen than multiple or maxFiles leaves room for — in the product's words ("No more files can be added."). Empty reads the product's words; with neither, the row is drawn without a sentence and that is reported. Not read while rows is given: there the product writes each row's errorText.
announcement string "" A message the product has assistive technology hear through the file upload's one live region, when it sets or changes it ("3 files added", "2 files were too large"). It is not drawn. Set in the same turn as rows change, it is said in place of what those rows would have said one by one.
disabled boolean false Nothing can be chosen or dropped: the trigger is disabled and the area answers nothing. Inside a Field, disable the Field. — File picker
error boolean false Puts the file picker in the error condition: the drop area's edge is drawn in the error role and the trigger is exposed as invalid. A Field sets it with its own error; the words are the Field's errorText. The file picker never sets it itself. — File picker
required boolean false Exposes the trigger as required, where the platform's button has such a state. A Field that is required sets it. It draws nothing — the mark beside the label is the Field's — and it validates nothing: the file picker has no value that could be missing. — File picker

Slots

Slot Descripción
trigger The product's own Button that opens the picker, with the product's own label ("Choose files"), variant and size. Exactly one. The file picker gives it the behaviour and never restyles it. — File picker
icon A glyph in place of the upload glyph at the top of the drop area (a picture for a photo, a table for a spreadsheet). Decorative; ignored in the button layout. — File picker

Eventos

Evento Descripción
select Files were chosen in the picker or dropped on the area, were passed by the file picker (its select, FPK-9) and found room. Emitted once per choice by the file upload itself — the file picker's own event does not leave it (FUP-57) — with those files as the platform's own handles, in the order the platform gave them. While rows is given the product adds a row for each and does with the files what it means to, and the file upload keeps none of them. While rows is own the file upload has already kept them and drawn their rows, the event carries the files it newly kept — never one it already had — and the product need not listen. It reads none of them in either case.
reject Files of one choice or one drop were refused, by the file picker (its reject, FPK-9) or by the file upload for want of room. Emitted once, after select where both happen, with each refused file and its reason: type (not among accept, or a folder), size (larger than maxSize) or count (more than one while multiple is false, or more than maxFiles leaves room for). The reasons are names for the product's code, not words: while rows is given the product says what happened, with a Field's errorText, a failed row or announcement; while rows is own the file upload has already drawn a row for each refused file, with the product's sentence for its reason.
change The files the file upload keeps changed by what a person did: a choice or a drop that kept at least one file or replaced the one kept, or a kept file removed by its row's remove action. Emitted once, after select and reject where they happen, with the files now kept, in order, as the platform's own handles. Emitted only while rows is own, and never for what the product did itself (a file handed over, the kept files cleared, a form reset, a change of rows).
move While rows is given and reorderable is true: a person asked for a row to stand in another place, by the move menu, the keyboard or a drag (FUP-59). Carries from, the row's index when the move began, and to, the index it is to have, both counted from 0. Emitted once per move; the file upload has changed nothing — the product puts its file item in the new place. While rows is own the file upload makes the move itself and emits change instead.

Elementos

Los tipos de elemento que el componente repite. Un fixture enumera sus elementos en orden; cada plataforma nombra cada tipo según su propio estilo.

file

One chosen file as a row of the list: a File item, with the props a row sets for itself. Its form, whether it draws a picture and how, the two action names and whether it is disabled are the file upload's, given to every row (FUP-58). While rows is given the product adds it, changes it and removes it, and the file upload only arranges it; while rows is own the file upload makes one itself for each file it keeps and for each file it refused, and the product gives none.

Propiedad Tipo Valor predeterminado Descripción
value string "" What identifies the row to the product: given back by click, remove and retry. Unique within one file upload.
name string "" The file's name as the person knows it ("report-2026.pdf"). The file item's name for assistive technology in every form, also where it is not drawn.
mediaType string "" The file's kind as the platform reports it ("image/png"), from which the glyph is chosen. Empty, the kind is read from the end of name.
glyph string "" The name of a glyph of the system's icon set, drawn in place of the one the file's format would choose. Empty lets the format choose.
formatText string "" The file's format in the product's words ("PDF", "Spreadsheet"), drawn first on the second line. Empty draws none: the file item has no word for a format.
sizeText string "" The file's size as text, formatted by the product for the reader's locale ("1.2 MB"). Empty draws none.
status pending | uploading | done | error "pending" Where the file stands. pending: chosen or kept, and nothing is happening to it — the whole life of a file in a form that is submitted as one, and of a file that is only shown. uploading: the product is sending it; the bar is drawn. done: the product has it. error: it failed or was refused; errorText says why.
statusText string "" The status in the product's words ("Waiting", "Uploading", "Uploaded"), drawn last on the second line and read with the file. The mark and its color are never the only sign of a status.
progress number null How far the sending has come, from 0 to 1, while status is uploading. Null draws the bar as under way without saying how far. Ignored in every other status.
errorText string "" Why the file failed and what to do, in the product's words ("Larger than 10 MB", "The connection was lost"). Drawn in the status's place while status is error, in every form, and never cut.
retryable boolean false Whether a failed file offers the retry action. True for a failure that trying again may cure (a lost connection); false for one it cannot (a file that is too large).
previewSrc string "" The address of a picture of the file, as the platform's images take one: a small rendition the product's server made of a file it already has. With preview it is drawn for a file of any format. Empty, the picture comes from the file itself where the product gave it.
interactive boolean false Makes the body — the glyph or the picture, and the texts — one pressable action that emits click, for a product that opens the file where it is: an image in an Image preview, a document in a viewer. The actions stay their own. A file item whose action is to go to the file takes href instead and is a link.
href string "" The destination: the address of the file, as the platform's links take one. Not empty, it makes the body a link — to a file the product already has, to open or to download — whether interactive is set or not.
target same | new "same" Where the destination opens. same: where the person is. new: a new context — a new tab or window on the web, outside the current screen elsewhere — which a row marks and targetLabel says. Ignored without href.
targetLabel string "" What assistive technology reads after the link's name while target is new ("opens in a new tab"). The product's words.
readOnly boolean false The file is shown and cannot be taken back: no remove action and no retry action is drawn. For a file of a record that is only read. The body can still be pressed where interactive or href says so.
Evento Descripción
click The File item's click: the person activated the body of a row that is pressable or a link. Carries the row's value. The file upload opens nothing. Emitted only while rows is given: a row the file upload made itself is never pressable.
remove The File item's remove: the person asked to remove this row, by its remove action. Carries the row's value. The product removes the row — and stops sending the file, if it was — and the file upload then moves focus (FUP-30). The file upload removes nothing itself. Emitted only while rows is given: a row the file upload made for a file it keeps is removed by the file upload, which emits change.
retry The File item's retry: the person asked to try a failed row again, by its retry action. Carries the row's value. The product sends the file again and sets the row's status. Emitted only while rows is given: a row the file upload made itself has no retry action.
Especificación Web SwiftUI Jetpack Compose Flutter React Native
Elemento tanrei-file-item TanreiFileItem TanreiFileItem TanreiFileItem FileItem
value · Propiedad value id key key value
name · Propiedad name name name name name
mediaType · Propiedad mediaType mediaType mediaType mediaType mediaType
glyph · Propiedad glyph glyph glyph glyph glyph
formatText · Propiedad formatText formatText formatText formatText formatText
sizeText · Propiedad sizeText sizeText sizeText sizeText sizeText
status · Propiedad status status status status status
statusText · Propiedad statusText statusText statusText statusText statusText
progress · Propiedad progress progress progress progress progress
errorText · Propiedad errorText errorText errorText errorText errorText
retryable · Propiedad retryable isRetryable retryable retryable retryable
previewSrc · Propiedad previewSrc previewURL previewModel previewImage previewSource
interactive · Propiedad interactive action onClick onPressed onPress
href · Propiedad href destination href href href
target · Propiedad target opens target target target
targetLabel · Propiedad targetLabel targetLabel targetLabel targetLabel targetLabel
readOnly · Propiedad readOnly isReadOnly readOnly readOnly readOnly
click · Evento click action onClick onPressed onPress
remove · Evento remove onRemove onRemove onRemove onRemove
retry · Evento retry onRetry onRetry onRetry onRetry
Notas A tanrei-file-item child in the default slot, in the order of the rows, with the attributes of its own spec. Its file property (a File, never an attribute) is what a form submits for it. click, remove and retry bubble, with detail.value. Written by the product only while rows="given"; while rows is own the element renders the same element in its shadow root for each file it keeps. A value in the items array, identified by id. The product keeps the URL it was handed beside it. Passed only while the product gives the rows; left out, the file upload keeps its own files and draws their rows. A value in the items list, identified by key. The product keeps the Uri beside it. Passed only while the product gives the rows; left out, the file upload keeps its own files and draws their rows. A value in the items list, identified by key. Passed only while the product gives the rows; left out, the file upload keeps its own files and draws their rows. An object in the items array, identified by value. Passed only while the product gives the rows; left out, the file upload keeps its own files and draws their rows.

Estados

Comportamiento

Requisitos en formato EARS. Cada implementación de plataforma se prueba contra estos identificadores. El texto de los requisitos se muestra tal como está en la especificación, en inglés.

ID Requisito
FUP-1 THE file upload SHALL lay out, from top to bottom and from its start edge: its file picker, laid out and drawn as the file picker's own sentences say (FPK-1 to FPK-4), and, WHILE it holds at least one row, the list of rows comp.file-upload.list.gap below it (FUP-17). It SHALL be as wide as its layout gives it and as tall as its parts, SHALL draw no container, fill or edge around the whole, and SHALL draw no label, no help, no error text and no requirement mark of its own: those are the Field's (FUP-32).
FUP-2 WHILE the list holds no row THE file upload SHALL give its file picker emptyText as the text that stands beside the trigger (selectionText, FPK-2), and WHILE the list holds a row SHALL give it none: the rows say what is chosen. Whether nothing is chosen is the file upload's to know; where that text stands, how it is drawn, how it wraps and that the drop-area layout draws none are the file picker's (FPK-2, FPK-3).
FUP-7 THE file upload SHALL NOT upload, as its file picker does not (FPK-7) and its file items do not (FIT-33): it SHALL open no connection and send no file or part of one to anyone, SHALL NOT read a file's bytes — not to check its kind, not to measure it, not to draw a picture of it — and SHALL NOT copy or store a file. The picture of an image is drawn only WHILE itemPreview is true, by the file item and the platform and as FIT-6 and FIT-7 say; the file upload makes no address and no picture itself. WHILE rows is given it SHALL NOT keep a file beyond handing it to the product, and SHALL draw of a file only what the product gives back as a file item (FUP-17); WHILE rows is own it SHALL keep of a file only the platform's own handle of it, for as long as FUP-51 says, and SHALL draw of it only what FUP-44 and FUP-45 name. This holds on every platform, as a component that draws a destination loads nothing about it.
FUP-9 WHEN its file picker reports a choice or a drop — select with the files that passed accept, maxSize and the rule of one file, reject with those it refused (FPK-9) — WHILE rows is given THE file upload SHALL keep none of them and SHALL judge what the file picker cannot know, the room that is left: WHILE multiple is true and maxFiles is a number, it SHALL refuse with the reason count each file that passed beyond maxFiles less the rows the list already holds. It SHALL then emit select once with the files that found room, WHERE there are any, and reject once, WHERE there are any refused, with the files the file picker refused in the order it refused them, then the files refused for want of room in the order the platform gave them, each with its reason; in that order (FUP-57). THE file upload SHALL add no row, replace no row, draw no error and say no word because of a choice: the product adds a row for each file it keeps, and WHILE multiple is false replaces the row it had. WHILE rows is own the file upload keeps what found room (FUP-43).
FUP-16 WHILE error is true THE file upload SHALL give error to its file picker, which draws the drop area's edge for it and exposes the trigger as invalid (FPK-16), and SHALL draw nothing else for it: no file item changes under the error condition. THE file upload SHALL never set error itself: not for a refused file, not for a failed row, not for a missing value.
FUP-17 WHILE the file upload holds at least one row THE file upload SHALL draw each row as one file item (FUP-58) inside one item of its List, named by the file's name — a row of this spec is one file item, whatever its form — in the order the product gave them WHILE rows is given and in the order of FUP-44 and FUP-45 WHILE rows is own: WHILE itemForm is row as a vertical List, comp.list.gap.small apart (the List's small gap), each as wide as the file upload; WHILE itemForm is tile as a horizontal List that wraps (LST-2) — lines from the start edge that wrap onto a further line where the width ends, comp.list.gap.small apart along both directions, the tiles of one line aligned at their tops, each of the size itemWidth and itemHeight give (FIT-3). How a file item is laid out and drawn, and that the name of a row is never cut, are the file item's (FIT-2 to FIT-10). THE file upload SHALL NOT sort, group, number or count the rows, WHILE rows is given SHALL NOT add or remove one itself, SHALL draw no line and no fill between or behind them, and WHILE reorderable is false SHALL give them no order a person can change. WHILE it holds no row THE file upload SHALL draw no list, keep no room for one and expose none. WHEN itemForm names a value the file upload does not know THE file upload SHALL arrange the rows as a column and in debug builds report it.
FUP-21 WHEN a file item of the file upload emits click, remove or retry (FIT-11 to FIT-13) WHILE rows is given THE file upload SHALL let the event reach the product as the row's own, with the row's value, and SHALL do nothing else: it SHALL NOT open a file, remove the row, change its status or move focus because of the activation. WHILE rows is own it answers a row's remove itself (FUP-46), and no row is pressable or has a retry action (FUP-44). The stops of the rows SHALL follow the trigger in the focus order, in the order of the rows, each row's stops in the order the file item gives them — its body where it is pressable, retry, remove; the file upload SHALL add no stop of its own, and SHALL handle no arrow key, Delete or Backspace.
FUP-24 THE file upload SHALL expose itself to assistive technology as a group named by label — or, inside a Field, by the Field's label WHILE label is empty (FUP-32) — holding what its file picker exposes and, WHILE there are rows, a list of them. It SHALL give its file picker no name, so that the file picker exposes no second group inside this one (FPK-17), and SHALL give it what a Field gives the file upload (FUP-32) as the last part of the trigger's description; the rest of that description — constraintsText, then emptyText WHILE it is drawn (FUP-2) — and the trigger's required and invalid states are exposed by the file picker (FPK-17). WHILE label is empty and no Field names it THE file upload SHALL still render and in debug builds report that the group has no accessible name.
FUP-25 THE file upload SHALL expose its rows as one list that takes the group's name — its List's (LST-18) — and each row as one item of that list: the List's item, holding the file item, which is then exposed as the group it is when it stands alone (FIT-19) and not as a list item of its own. What a row reads — the name, the format, the size, the status or the error, the progress bar while it is sent, and the actions named removeLabel and retryLabel with the file's name in them — is the file item's (FIT-19). A platform without a list role SHALL expose a container that holds the rows in order and state that in its README.
FUP-26 THE file upload SHALL have exactly one live region, polite and never assertive, for everything it announces, and SHALL announce through it, once each and in the product's words alone: WHEN a row is added after the file upload was first presented, the row's name and its statusText; WHEN a row's status becomes done, its name and its statusText; WHEN a row's status becomes error, or its errorText changes while it is error, its name and its errorText; and WHEN the product sets announcement to a text that is not empty, or changes it, that text. Changes that arrive in one turn of the platform's rendering SHALL be said as one message, in the order of the rows; WHERE announcement is set or changed in that same turn THE file upload SHALL say announcement alone, so that forty files added are said in one sentence of the product's and not forty times. THE file upload SHALL NOT announce a change of progress, a removal, a drag over the area or, WHILE rows is given, a refused file by itself — the product says those with announcement where they must be said, and FUP-50 says what is announced WHILE rows is own — SHALL NOT announce rows that are present when it is first presented, and SHALL NOT move keyboard or screen-reader focus to announce.
FUP-27 THE file upload SHALL have no words of its own, as its file picker and its file items have none (FPK-18, FIT-20): it SHALL take emptyText from the instance's prop and, WHILE that is empty, from the product's words of the key noFileText; SHALL give removeLabel and retryLabel to every file item as they are given to it, where an empty one reads the product's words of the keys removeLabel and retryLabel and a missing one is reported by the file item (FIT-20); and SHALL take every other word — label, announcement, each row's texts, and the texts it passes to its file picker — from the product alone, and from nowhere else. IF emptyText is not given THE file upload SHALL give its file picker nothing in its place. IF maxFiles is a number WHILE constraintsText is empty THE file upload SHALL still render and in debug builds report that a limit is enforced and not said, as its file picker reports for accept and maxSize (FPK-18). THE file upload SHALL build no sentence from accept, maxSize, maxFiles, a count of rows or a size in bytes.
FUP-28 WHILE disabled is true THE file upload SHALL give disabled to its file picker, which disables the trigger, draws its own parts from their disabled tokens and answers neither a press nor a drag (FPK-19); SHALL give disabled to every file item, which disables its actions and its body and keeps what it shows of the file drawn as it is and readable by assistive technology (FIT-21): what was chosen is still what was chosen; and SHALL emit neither select nor reject, and no row's click, remove or retry. WHEN a disabled file upload first appears its trigger SHALL be drawn disabled in its first frame, from its first render, without moving from the enabled look into it.
FUP-29 WHEN a row is added, is removed or changes its status THE file upload SHALL draw that at once, and WHILE reorderable is false without a motion of the file upload's own, the rows after it taking their new places at once — in a strip of tiles also where that moves a tile to another line; WHILE reorderable is true the rows that change place move as the List moves its items (LST-41). The changes of the drop area between states are the file picker's (FPK-20), and those of a row's body and actions the file item's (FIT-23).
FUP-30 WHEN a row leaves the list while focus is on one of its stops THE file upload SHALL move focus to the remove action of the row that followed it, or of the row before it WHERE none follows, or to the trigger WHERE no row is left, so that a person who removes several files in a row goes on from where they were; WHERE the row it would go to has no remove action (a read-only file item), to that row's body WHERE it is pressable, and otherwise on to the next such row or the trigger. Focus within a row that stays — a retry action that leaves — is the file item's (FIT-30). THE file upload SHALL NOT move focus because a row was added or changed otherwise. WHILE reorderable is true focus after a row left goes as the List says (LST-46): to the handle of the row that now stands where it stood, else of the row before it, else to the trigger.
FUP-31 WHERE the platform has forms that submit named values (the web) WHILE rows is given THE file upload SHALL take part in its form as one control: it SHALL submit under its own name, in the order of the rows, the file of every row the product gave a file to — as the platform's own handle of that file, the one select carried, given to the row in the platform's idiom and not as a prop of this spec, since a handle is no value a fixture or an attribute can hold — WHILE the file upload is not disabled, and nothing otherwise; SHALL be disabled WHILE the form disables the set it is in; SHALL report a missing value to the form's own validation, with the platform's own message, WHILE required is true and no row carries a file; and SHALL change nothing WHEN the form is reset, because the rows are the product's to clear. It SHALL submit a file whatever the row's status: a product that sends files itself gives the file upload no name. WHILE rows is own the form is given the files the file upload keeps (FUP-49).
FUP-32 WHILE the file upload is the control of a Field THE file upload SHALL take from it what a Field gives its control (FLD-8, FLD-10, FLD-11, FLD-14): the Field's label as the group's name WHILE its own label is empty; the Field's supporting text with, in error, the error text after it and optional text, which it SHALL pass to its file picker as the last part of the trigger's description (FUP-24); the invalid state as its own error condition (FUP-16); the required state as required; and the disabled state as disabled. It SHALL pass the three states on to its file picker by the exchange a Field has with a control (FPK-21); the Field's one control SHALL be the file upload, and the file picker inside it SHALL take nothing from the Field directly. WHEN the label of the Field is pressed THE file upload SHALL move focus to the trigger and open nothing.
FUP-33 WHILE the text size is enlarged THE file upload SHALL grow with its parts: its file items as FIT-24 says and its file picker as FPK-22 says, a strip of tiles wrapping sooner and never running past the file upload's width. At 200 % in a width of 320 no text SHALL be lost or lie over another part, and nothing SHALL need scrolling in two directions.
FUP-34 WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE file upload SHALL draw nothing of its own that rests on a color: its rows are drawn as FIT-25 says and its drop area as FPK-23 says.
FUP-35 WHERE the layout direction is right-to-left THE file upload SHALL mirror its arrangement as its List mirrors (LST-5): a strip of tiles begins at the right and runs to the left on every line, and each row is mirrored inside as FIT-26 says; its file picker mirrors as FPK-24 says.
FUP-36 THE file upload SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the file upload is shown THE file upload SHALL draw itself for the new direction at once, without a motion of its own, keeping its rows and its focus.
FUP-37 THE file upload SHALL leave the direction of each row's texts and the mirroring of each row's glyphs to the file item (FIT-28), and those of its file picker's texts, emptyText among them, to the file picker (FPK-26); THE file upload SHALL mirror no glyph itself.
FUP-38 IF maxFiles is not a number above zero THE file upload SHALL set no such limit; IF two rows have the same value THE file upload SHALL still draw both; and in each case debug builds SHALL report it. A status, a fit or a size that a row does not know is answered by the file item (FIT-29); a layout it does not know, a maxSize that is no number above zero and an entry of accept that cannot be read are answered by the file picker (FPK-27).
FUP-39 WHEN a prop of the file upload or of a row changes while it is shown THE file upload SHALL draw and expose the new value at once, keeping its focus, SHALL give a prop that is its file picker's to the file picker and a prop that is its file items' to every file item in the same turn (FPK-28, FIT-30), and SHALL announce only what FUP-26 names. WHEN accept, maxSize, maxFiles or multiple changes THE file upload SHALL judge the room by the new value from the next choice on and SHALL NOT judge again, mark or remove the rows it already holds.
FUP-40 WHILE the system's icon set cannot yet draw a glyph (its font is still loading) THE file upload SHALL draw nothing of its own in a glyph's place: the glyphs of a row are the file item's (FIT-31) and the glyph of the drop area the file picker's (FPK-29).
FUP-41 IF the file upload holds a child that is neither its trigger, its glyph nor a file item THE file upload SHALL NOT draw it and in debug builds report it; a trigger that is not a Button is answered by the file picker (FPK-30). A platform that cannot inspect its content SHALL state the rule in its README and docs page.
FUP-42 THE file upload SHALL hold its rows in one of two ways, as rows names. own, the default: it keeps the files a person chose and draws a row for each itself (FUP-43 to FUP-51), so that a form works with the file upload placed in it and nothing written. given: the product gives the rows, and the file upload draws them and keeps no file, as FUP-9, FUP-17, FUP-21 and FUP-31 say. The product gives rows by saying so in the platform's idiom — on the web by the value given of rows; where rows are a list the product passes (SwiftUI, Compose, Flutter, React Native) by passing that list, an empty one included, and by leaving it out for own — and never by what the list happens to hold: a product that owns its rows owns them while it has none. WHILE rows is own THE file upload SHALL NOT draw a row the product gives and in debug builds SHALL report it, naming rows; WHILE rows is given it SHALL keep no file, SHALL give nothing to read and take nothing by FUP-48, and in debug builds SHALL report a file handed to it that way. WHEN rows names a value the file upload does not know THE file upload SHALL behave as own and in debug builds report it.
FUP-43 WHEN files arrive — chosen in the picker or dropped on the area, and judged by its file picker (FPK-9), or handed over by the product and judged by the same rule (FUP-48, FPK-31) — WHILE rows is own THE file upload SHALL first remove the rows of the files an earlier arrival refused (FUP-45); SHALL pass over, as if it had not arrived, a file that passed and that it already keeps, and a second copy of a file within the same arrival — the same file being one with the same name, size and time of last change as the platform reports them, or the same location where the platform hands over locations — so that no file is kept twice and none is refused for being chosen again; and, WHILE multiple is true and maxFiles is a number, SHALL refuse with the reason count each remaining file beyond maxFiles less the files it keeps, in the order the platform gave them. WHILE multiple is true it SHALL keep each file that passed after the files it already keeps, in the order the platform gave them: a second choice adds to the first and replaces nothing. WHILE multiple is false and a file passed, it SHALL let go of every file it keeps and keep the new one in their place; a choice that is refused SHALL replace nothing. It SHALL then draw a row for each file it keeps (FUP-44) and for each file it refused (FUP-45) and emit as FUP-47 says. It SHALL NOT move focus because files arrived, except as FUP-30 says where a row that held focus left.
FUP-44 WHILE rows is own THE file upload SHALL draw each file it keeps as a row, in the order it keeps them: a file item (FIT-1 to FIT-33), in the form the file upload gives every row (FUP-58), that is given the platform's handle of the file, whose name and mediaType are the file's as the platform reports them, whose status is pending, and whose formatText, statusText, errorText, progress and previewSrc are not given — the glyph of the file's format, or its picture WHILE itemPreview is true and the file is an image (FIT-6), the name, no status in words, no bar, and the remove action alone: never a retry action, because the file upload sends nothing that could be tried again, and never a pressable body or a link, because the file upload opens nothing and has no address of the file. As that row's sizeText it SHALL draw what the product's own function writes for the file's size in bytes, WHERE the product gave the file upload such a function in the platform's idiom, and nothing WHERE it gave none: THE file upload SHALL NOT write a size itself, because a number with a unit is words, and whether a megabyte is 1000 kilobytes or 1024 is the product's to say. A function that was not given SHALL NOT be reported: a row is whole with its name. THE file upload SHALL draw no status, no progress and no result of a sending on a row of a file it keeps, whatever the product does with the file afterwards.
FUP-45 WHILE rows is own THE file upload SHALL draw each file the last arrival refused as a row after the rows of the files it keeps — first the files its file picker refused, in the order it refused them, then the files refused for want of room, in the order the platform gave them — as a file item whose status is error, which is not retryable, and which is given no handle of the file and so draws no picture: the glyph of the file's format, the file's name, its size as FUP-44 says, the remove action, and the error mark before its errorText, which is the product's sentence for the reason — typeErrorText for type, sizeErrorText for size, countErrorText for count (FUP-53). A refused file SHALL NOT be kept: it is not among the files the product reads (FUP-48), is not submitted (FUP-49), does not count toward maxFiles and does not satisfy required. Its row SHALL stay until the person removes it (FUP-46), until files next arrive (FUP-43), or until the kept files are cleared (FUP-48, FUP-49), and THE file upload SHALL NOT set its own error condition for it (FUP-16). WHERE the platform delivers no drop over an area that refuses (the web, once the file picker has told it that letting go there does nothing, FPK-13) no file arrived: THE file upload SHALL draw no row and say nothing for it, and the area's refusing look is what the person was told. IF neither the instance nor the product's words give the sentence for a reason THE file upload SHALL still draw the row, with its name and the error mark, and in debug builds SHALL report the missing word once per page or screen, naming the prop.
FUP-46 WHEN the person activates the remove action of a row by pointer, touch, Enter or Space WHILE rows is own THE file upload SHALL remove that row itself, at once and without asking again; WHERE the row was a file it keeps, SHALL let go of the file and emit change exactly once (FUP-47); WHERE it was a refused file, SHALL emit nothing, because the files it keeps did not change; and SHALL move focus as FUP-30 says: to the remove action of the row that followed, or of the row before WHERE none follows, or to the trigger WHERE no row is left. It SHALL emit neither remove nor retry: those are events of the product's rows, and here there are none.
FUP-47 WHILE rows is own THE file upload SHALL emit for one choice or one drop, once the files it keeps and the rows are as FUP-43 leaves them, and in this order: select once with the files it newly kept, WHERE there are any; reject once with the refused files and their reasons, WHERE there are any, as FUP-9 says; and change once, WHERE the files it keeps are not the same as before. WHEN the person removes a file it keeps (FUP-46) it SHALL emit change and nothing else. change SHALL carry the files now kept, in order, as the platform's own handles. THE file upload SHALL emit none of the three for what the product did itself — files handed over or the kept files cleared (FUP-48), a form reset (FUP-49), a change of rows (FUP-52) — as a control's events tell what a person did.
FUP-48 WHILE rows is own THE file upload SHALL offer the product, in the platform's idiom: the files it keeps, to read at any time, in order and as the platform's own handles — the product may send them itself, then or later, and the file upload draws nothing of that (FUP-44); a way to hand it files, which it SHALL have its file picker judge (FPK-31) and SHALL then treat as an arrival (FUP-43) — kept or refused, drawn and announced (FUP-50) as a choice is, and without an event (FUP-47) — answering with the refused files and their reasons; and a way to clear, which SHALL let go of every file it keeps and remove every row, kept and refused, SHALL announce nothing, and SHALL move focus by FUP-30 WHERE a row held it. What it gives to read SHALL be a copy of its list: changing the copy SHALL change nothing that is kept. All three SHALL work WHILE disabled is true: disabled stops the person, not the product.
FUP-49 WHERE the platform has forms that submit named values (the web) WHILE rows is own THE file upload SHALL take part in its form as one control: it SHALL submit under its own name every file it keeps, in the order it keeps them and as the platform's own handles, WHILE the file upload is not disabled, and nothing otherwise — never a refused file, and no empty entry while it keeps none; SHALL be disabled WHILE the form disables the set it is in; and SHALL report a missing value to the form's own validation, with the platform's own message, WHILE required is true and it keeps no file, and no longer once it keeps one. WHEN the form is reset THE file upload SHALL do what clearing does (FUP-48). WHEN the form is submitted THE file upload SHALL keep its files and its rows: what follows a submission is the product's. THE file upload SHALL give the platform nothing to restore: WHEN a page is restored — a return through history, a session brought back — it SHALL begin with no file and no row and say nothing of the files it had, because a handle does not outlast the page and a row for a file that can no longer be sent would be untrue. WHILE it has no name THE file upload SHALL submit nothing and still keep and draw its files, for a product that reads them (FUP-48) and sends them itself.
FUP-50 WHILE rows is own THE file upload SHALL announce through the one live region of FUP-26, politely and once for each arrival: the name of each file it newly kept and, for each refused file, its name and its sentence (FUP-45), as one message in the order of the rows. The names are the files' own and the sentences the product's, so that the file upload still says no word of its own. WHERE the product sets or changes announcement in the same turn — in its answer to select, reject or change — THE file upload SHALL say announcement alone. THE file upload SHALL NOT announce a removal, a clearing or a reset: after a removal focus rests on the next row's remove action, which is described by that file's name, or on the trigger, whose description holds emptyText once no row is left (FUP-24, FUP-30). It SHALL NOT announce a file it passed over (FUP-43), and SHALL build no count of files.
FUP-51 THE file upload SHALL hold a file it keeps as the platform's own handle and nothing more: it SHALL NOT read it, copy it, make an address or a picture for it, or measure it beyond the size the platform reports (FUP-7). It SHALL give the handle to the file item that draws the file, which makes nothing of it WHILE itemPreview is false, so that a hundred files kept cost a hundred handles; WHILE itemPreview is true the file item of an image makes what the platform needs to draw its picture and releases it as FIT-7 says — only once that row is in or near what is in view, at most one for a row, and never beyond the life of the row. It SHALL let go of a handle, and take it from the row's file item, WHEN its row is removed (FUP-46), WHEN another file takes its place (FUP-43), WHEN the kept files are cleared or the form is reset (FUP-48, FUP-49) and WHEN rows becomes given (FUP-52); and SHALL keep its files WHILE it is taken out of the page or the screen and put back, as a control keeps its value.
FUP-52 WHEN rows changes from own to given while the file upload is shown THE file upload SHALL let go of every file it keeps, remove every row it drew itself, and draw the rows the product gives from then on; WHEN it changes from given to own THE file upload SHALL begin with no kept file and stop drawing the product's rows (FUP-42). In both directions it SHALL emit nothing and announce nothing, SHALL move focus to the trigger WHERE a row that leaves held it, and SHALL carry no file across by itself: a product that changes its mind reads the kept files first and gives them to rows of its own, or hands the files of its rows over (FUP-48).
FUP-53 THE file upload SHALL take typeErrorText, sizeErrorText and countErrorText from the instance's prop and, WHILE that is empty, from the product's words of the keys fileTypeErrorText, fileSizeErrorText and fileCountErrorText, and from nowhere else (FUP-27); SHALL read them only WHILE rows is own, and need one only once a file was refused for its reason; and SHALL build no sentence for a refusal out of accept, maxSize, maxFiles or a file's kind or size.
FUP-54 WHILE rows is own everything this spec and the file item's say of a row, of the list and of what stands around them SHALL hold for the rows the file upload makes itself as for the product's: the arrangement (FUP-17); the file item's layout, its name that a row never cuts, the states of its actions, their focus ring and their target (FIT-2, FIT-3, FIT-14, FIT-18); emptyText while the list holds no row (FUP-2); disabled — the rows stay readable, the remove actions are disabled and leave the focus order, and the person chooses, drops and removes nothing (FUP-28); rows that appear and leave at once, without a motion (FUP-29); enlarged text (FUP-33); forced colors (FUP-34); and direction, with each file's name laid out in its own (FUP-35 to FUP-37). WHILE disabled is true THE file upload SHALL emit no change.
FUP-55 WHEN accept, maxSize, maxFiles or multiple changes WHILE rows is own THE file upload SHALL judge by the new value from the next arrival on and SHALL NOT judge again, mark or let go of a file it keeps (FUP-39): files kept under multiple stay WHEN multiple becomes false, and the next file that passes takes the place of them all (FUP-43). WHEN the product's function for sizes or one of the three sentences changes THE file upload SHALL draw the rows it holds with the new text at once and announce nothing.
FUP-56 THE file upload SHALL hold exactly one file picker, as its member, and SHALL build no choosing of its own: the Button and the glyph the product places in the file upload's trigger and icon slots SHALL be the file picker's trigger and glyph (FPK-5, FPK-4), and layout, multiple, accept, maxSize, constraintsText, dropText, disabled, error and required SHALL be given to the file picker as they are given to the file upload. THE file upload SHALL give its file picker no maxFiles, because the room that is left is measured against what the file upload holds (FUP-9, FUP-43), and no label (FUP-24). THE file upload SHALL draw the file picker from the file picker's own tokens and SHALL give it no look, no state and no size that a file picker standing alone does not have. Everything the file picker's sentences say (FPK-1 to FPK-31) SHALL hold for it inside the file upload, with two differences: it is named, described and put in its states by the file upload (FUP-24, FUP-32), and its two events are answered by the file upload (FUP-57).
FUP-57 WHEN its file picker emits select or reject THE file upload SHALL answer them itself and SHALL NOT let them reach the product as they are: the product SHALL hear the file upload's own select, reject and change (FUP-9, FUP-47), which tell what the file upload did with the choice — the files that also found room and, WHILE rows is own, the files it newly kept — so that one choice is told once.
FUP-58 THE file upload SHALL draw every row as a file item, its member, and SHALL build no row of its own: WHILE rows is given the file items are the product's own, placed in the file upload in the platform's idiom, and WHILE rows is own the file upload makes one for each file it keeps and for each file it refused (FUP-44, FUP-45). It SHALL give every file item it holds, the product's included, form as itemForm, preview as itemPreview, fit as itemFit, nameHidden as itemNameHidden, width as itemWidth, height as itemHeight, removeLabel and retryLabel as its own, and disabled WHILE it is disabled itself, so that the rows of one file upload are of one form; a file item of the product's that sets one of these otherwise SHALL be drawn as the file upload says, and in debug builds reported. Everything else of a file item — its name, its format and its glyph, its texts, its status, the address of its picture, whether it is pressable or a link, whether it is read-only — SHALL stay the row's own. THE file upload SHALL draw a file item from the file item's own tokens and SHALL give it no look, no state and no size that a file item standing alone does not have. Everything the file item's sentences say (FIT-1 to FIT-33) SHALL hold for it inside the file upload, with three differences: it is an item of the file upload's list (FUP-25); what is said of it is said through the file upload's one live region (FUP-26, FUP-50); and WHILE rows is own its remove is answered by the file upload (FUP-46).
FUP-59 WHILE reorderable is true THE file upload SHALL make its List reorderable (LST-31 to LST-47) — a handle on every row, the whole row dragged by a pointer, the move menu from the handle and the keyboard from the handle — with the nine words given to it as they are given to the file upload, and WHEN the List emits move (LST-33, LST-35, LST-39): WHILE rows is given THE file upload SHALL emit move once with the same from and to and change nothing, the product's file items being the product's to put in their new place; WHILE rows is own THE file upload SHALL put the file in its new place among the files it keeps, draw the rows in the new order, submit the kept files in that order (FUP-49) and emit change exactly once with the files now kept (FUP-47), and SHALL change nothing for a move that would put the row of a refused file among the rows of the files it keeps, or the row of a kept file after a refused one (FUP-45), so that the List takes it as refused (LST-43). WHILE the file upload is disabled THE file upload SHALL change no order and emit no move, as it emits no other event (FUP-28).
FUP-60 WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE file upload SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on its group (FUP-24) — an id reference by the elements it names in the host's tree or a tree around it, a description after its own — except those the file upload owns (aria-label, aria-labelledby, aria-disabled, aria-required, aria-invalid, aria-errormessage), which it SHALL move and not expose: its name is label, or its Field's (FUP-24, FUP-32), and its states are its props, which its file picker exposes (FPK-17). WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE file upload SHALL take it off that element. WHEN the product sets an aria- attribute the file upload owns THE file upload SHALL report it in debug builds.

Correspondencia de nombres por plataforma

Paridad no significa API idénticas. Cada plataforma expresa el mismo contrato con su propio estilo.

Especificación Web SwiftUI Jetpack Compose Flutter React Native
Componente tanrei-file-upload TanreiFileUpload TanreiFileUpload TanreiFileUpload FileUpload
layout · Propiedad layout layout layout layout layout
rows · Propiedad rows items items items items
itemForm · Propiedad itemForm itemForm itemForm itemForm itemForm
itemPreview · Propiedad itemPreview itemPreview itemPreview itemPreview itemPreview
itemFit · Propiedad itemFit itemFit itemFit itemFit itemFit
itemNameHidden · Propiedad itemNameHidden itemNameHidden itemNameHidden itemNameHidden itemNameHidden
itemWidth · Propiedad itemWidth itemWidth itemWidth itemWidth itemWidth
itemHeight · Propiedad itemHeight itemHeight itemHeight itemHeight itemHeight
reorderable · Propiedad reorderable onMove onMove onMove reorderable
multiple · Propiedad multiple allowsMultipleSelection multiple multiple multiple
accept · Propiedad accept allowedContentTypes mimeTypes accept accept
maxSize · Propiedad maxSize maxSize maxSize maxSize maxSize
maxFiles · Propiedad maxFiles maxFiles maxFiles maxFiles maxFiles
label · Propiedad label label contentDescription semanticLabel accessibilityLabel
constraintsText · Propiedad constraintsText constraintsText constraintsText constraintsText constraintsText
dropText · Propiedad dropText dropText dropText dropText dropText
emptyText · Propiedad emptyText emptyText emptyText emptyText emptyText
removeLabel · Propiedad removeLabel removeLabel removeContentDescription removeSemanticLabel removeAccessibilityLabel
retryLabel · Propiedad retryLabel retryLabel retryContentDescription retrySemanticLabel retryAccessibilityLabel
dragHandleLabel · Propiedad dragHandleLabel dragHandleLabel dragHandleLabel handleSemanticLabel dragHandleLabel
dragHandleHint · Propiedad dragHandleHint dragHandleHint dragHandleHint handleSemanticHint dragHandleHint
moveToStartLabel · Propiedad moveToStartLabel moveToStartLabel moveToStartLabel moveToStartLabel moveToStartLabel
moveBeforeLabel · Propiedad moveBeforeLabel moveBeforeLabel moveBeforeLabel moveBeforeLabel moveBeforeLabel
moveAfterLabel · Propiedad moveAfterLabel moveAfterLabel moveAfterLabel moveAfterLabel moveAfterLabel
moveToEndLabel · Propiedad moveToEndLabel moveToEndLabel moveToEndLabel moveToEndLabel moveToEndLabel
liftedText · Propiedad liftedText liftedText liftedText liftedText liftedText
movedText · Propiedad movedText movedText movedText movedText movedText
cancelledText · Propiedad cancelledText cancelledText cancelledText cancelledText cancelledText
typeErrorText · Propiedad typeErrorText typeErrorText typeErrorText typeErrorText typeErrorText
sizeErrorText · Propiedad sizeErrorText sizeErrorText sizeErrorText sizeErrorText sizeErrorText
countErrorText · Propiedad countErrorText countErrorText countErrorText countErrorText countErrorText
announcement · Propiedad announcement announcement announcement announcement announcement
disabled · Propiedad disabled disabled enabled enabled disabled
error · Propiedad error isError isError isError error
required · Propiedad required isRequired required required required
select · Evento select onSelect onSelect onSelect onSelect
reject · Evento reject onReject onReject onReject onReject
change · Evento change onChange onChange onChanged onChange
move · Evento move onMove onMove onMove onMove
Notas A row is a File item: **every row is a tanrei-file-item** — the product's own child in the default slot while rows="given", and one the element makes in its shadow root for each file it keeps or refused while rows is own. The six forwarded attributes item-form, item-preview, item-fit, item-name-hidden, item-width, item-height, with remove-label, retry-label and disabled, are given to every file item through an internal channel that is not part of the file item's public surface, and win over what a product's item says of them (FUP-58); through the same channel each item is told that it is not an item of a list itself (role="group", FUP-25): the List's items are. The list is a <tanrei-list part="list"> in the shadow root, one <tanrei-list-item> per row named by the file's name: vertical, or with item-form="tile" horizontal with wrap; its gap is small. While rows="given" the shadow root assigns its slots by hand (slotAssignment: "manual"): each product row to a slot of its own inside its list item, the trigger and the glyph to the two forwarding slots, and anything else to nothing (FUP-41). reorderable and the nine words (drag-handle-label …) are given to the List; the List's move is answered inside, and while rows="given" the file upload dispatches its own move (detail: { from, to }, bubbles). While rows is own each made item is given its File as its file property, and the item, not this element, makes and revokes the object URL of a picture (FIT-7, FUP-51); item-preview absent, none is ever made. A product's click, remove and retry listeners hear the items' own events, which bubble out of the light DOM; those of made items are handled inside and do not leave the shadow root. The parts of a made item leave the file upload under the prefix item- (exportparts="container: item-container, media: item-media, icon: item-icon, preview: item-preview, headline: item-headline, supporting-text: item-supporting-text, status-icon: item-status-icon, progress: item-progress, action: item-action"), since three of them would collide with the file picker's; a product's own item is styled as itself. A custom element with a shadow root that holds **its member's own element**. An instance: <tanrei-file-upload layout="drop-area" multiple accept=".pdf,image/png" max-size="10000000" drop-text="…" constraints-text="…" remove-label="…" name="attachments"><tanrei-button slot="trigger" variant="outlined">Choose files</tanrei-button></tanrei-file-upload>. The shadow root is <tanrei-file-picker exportparts="container, icon, label, supporting-text, selection-text"><slot name="trigger" slot="trigger"></slot><slot name="icon" slot="icon"></slot></tanrei-file-picker>, then the list and the live region. **Children passed on**: the product's trigger and glyph stay in the product's light DOM and reach the file picker through the two forwarding slots, so the trigger is still the product's own tanrei-button, focusable where it stands; the product's rows are assigned to slots of the file upload's own, one inside each list item. **Props**: the nine forwarded attributes are reflected onto the file picker as they change; max-files and label are not set on it, and selection-text is set to empty-text (or the product's noFileText) while the list holds no row. **Events**: the file picker's select and reject are heard on the member and stopped there; the file upload dispatches its own select, reject and change from its host (FUP-57). **Focus**: no delegatesFocus — the one stop the file picker has is the slotted trigger, which is outside every shadow root; a press on the Field's label calls focus() on it (FUP-32). **The Field's connection**: the file upload is the Field's control and asks for the connection; the file picker inside asks too, and the file upload answers it in the Field's place with the description and the three states and with no name (FUP-24, FPK-21). **Parts**: the file picker's parts leave under the file picker's names; list is the file upload's own. **Tokens**: nothing is written on the member — --<namespace>-comp-file-picker-… set on the file upload or above it is inherited into the file picker. The validation message is anchored on the slotted trigger, a descendant of the host. Form-associated (static formAssociated = true): ElementInternals.setFormValue receives a FormData with one entry per row that has a file, under the name attribute; setValidity({ valueMissing }) carries FUP-31 with the message of a native required file input. The live region is one visually hidden aria-live="polite" element; row changes are collected until the task that made them is over, and then written once. The file upload keeps its own files: the attribute rows is own (the default, also when absent or unknown) or given; the product's rows are drawn only with rows="given", and a tanrei-file-item child while rows is own is not drawn and is reported. <form><tanrei-file-upload name="attachments" multiple remove-label="…"><tanrei-button slot="trigger" variant="outlined">Choose files</tanrei-button></tanrei-file-upload></form> is the whole of a plain form's markup. While rows is own: the element keeps File objects in a private list and renders one tanrei-file-item for each, and for each refused file, in its own shadow root (the same row element, so a row is built once, in the file item; their remove is handled inside and does not leave the shadow root); the same file is one with equal name, size and lastModified. files is a read-only property returning a new File[] on each read; add(files: Iterable<File>): { file: File, reason: 'type' | 'size' | 'count' }[] hands files over and returns the refused; clear(): void empties; formatSize is a property, never an attribute, ((bytes: number) => string) | null, null by default — upload.formatSize = (bytes) => megabytes.format(bytes / 1e6). change is a CustomEvent that bubbles, detail.files a File[]; select and reject keep their details. Attributes type-error-text, size-error-text, count-error-text; left empty they read setWords({ fileTypeErrorText, fileSizeErrorText, fileCountErrorText }). The form: setFormValue(formData, null) with one entry per kept file under name — the second argument null, so that the browser has no state to restore, and formStateRestoreCallback restores nothing; formResetCallback does what clear() does; setValidity({ valueMissing }) while required and files is empty, anchored on the trigger, with the message of a native required file input. A native file input with no file submits one empty entry and this element submits none (FUP-49): a server that reads the field checks for both. A product that sends the files itself gives no name, reads files in its own submit handler, and — for a status and a bar on each row — uses rows="given". A drop over an area that refuses delivers no drop event in a browser (the file picker has set dropEffect to none), so nothing arrives and no refused row is drawn for it (FUP-45): a refused row comes from a choice in the picker, from a drop the area could not judge beforehand (a size, an extension, the room that is left) and from add(), which calls the file picker's judge() (FUP-48). A fixture whose rows is own and that holds file items cannot be markup alone, since a file is no attribute, and the fixture format has no field that asks for a step in script: the harness, the gallery and the documentation's live samples read rows and the items, and after the element is defined set formatSize and then call add() once with one File per file item, in order — named by the item's name, holding as many zero bytes as its place among the items (1, 2, 3 …), so that no two are the same file (FUP-43), and with one fixed lastModified. formatSize answers each of those sizes with its item's sizeText, and is left null where no item has one. The six own-* fixtures are the ones staged so; nothing is recorded before add() has returned and the rows are rendered. React: FileUpload takes FileItem children while the product gives the rows, and gains rows, onChange, formatSize, the three texts, and a ref with files, add() and clear(). Each row is a TanreiFileItem (FIT-1 to FIT-33), the same view a product places alone, given the six item props, the two action names and the disabled state by this view; the items the product passes are values of that view's props. The view is also built from TanreiFilePicker (FPK-1 to FPK-31) and passes its trigger, its glyph and the nine forwarded props on to it; what this note says of the picker, of accept and of the drop target is that component's, and stays here until this platform is built. TanreiFileUpload(items: [TanreiFileItem], layout: .dropArea, onSelect: { urls in … }) { TanreiButton("Choose files") { } }. The picker is fileImporter(isPresented:allowedContentTypes:allowsMultipleSelection:); accept is a list of UTType (an extension or a media type is resolved to one), and the files are security-scoped URLs the product opens and releases. The drop area is dropDestination(for: URL.self) with its isTargeted binding for the receiving condition; SwiftUI tells the kinds before the drop, not sizes. With VoiceOver the area is a drop point named by dropText. disabled follows .disabled(_:) and the environment; the Field's name, description and states arrive through the environment. The announcement is AccessibilityNotification.Announcement, one per turn of the run loop. No form submission (FUP-31 does not apply). rows is which initialiser is used — TanreiFileUpload(items:) is given, also with an empty array; without items the view keeps its own files. TanreiFileUpload(files: $urls) { TanreiButton("Choose files") { } } binds the kept files to the product's [URL] (reading them is reading the binding; handing over and clearing are writing it, judged by FUP-43), and without the binding the view keeps them in its own state for onChange to report. The view starts and stops security-scoped access for a URL it keeps and releases it when it lets go (FUP-51). formatSize: ((Int64) -> String)?; the same file is the same standardised URL. No form: FUP-49 does not apply, and a product reads the binding when it sends. The rows arranged by the List and the reorderable form (FUP-59) are not built on this platform yet. Each row is a TanreiFileItem (FIT-1 to FIT-33), the same view a product places alone, given the six item props, the two action names and the disabled state by this view; the items the product passes are values of that view's props. The view is also built from TanreiFilePicker (FPK-1 to FPK-31) and passes its trigger, its glyph and the nine forwarded props on to it; what this note says of the picker, of accept and of the drop target is that component's, and stays here until this platform is built. TanreiFileUpload(items = …, onSelect = { uris -> … }, trigger = { TanreiButton(onClick = {}) { Text("Choose files") } }). The picker is rememberLauncherForActivityResult with OpenDocument or OpenMultipleDocuments; accept is given as MIME types (an extension is resolved through MimeTypeMap), and the files are content Uris whose name and size come from the resolver's metadata, not from reading them. The drop area is Modifier.dragAndDropTarget, which is meaningful on large screens and with a pointer; on a phone the drop-area layout is still drawn and still pressed. disabled is the inverse flag enabled. The group is semantics(mergeDescendants = false) { contentDescription }; rows carry CollectionItemInfo; the announcement is a liveRegion = Polite node. No form submission. rows is whether items is passed — items = null (the default) is own, a list, also an empty one, is given. TanreiFileUpload(state = rememberTanreiFileUploadState(), trigger = { … }): the state holder has files: List<Uri> (a snapshot), add(uris) and clear(), and survives recomposition, not process death (FUP-49's restoration: it begins empty). formatSize: ((Long) -> String)? = null; the same file is the same Uri. A kept Uri is held with the read permission the picker granted, and nothing is opened. No form. The rows arranged by the List and the reorderable form (FUP-59) are not built on this platform yet. Each row is a TanreiFileItem (FIT-1 to FIT-33), the same view a product places alone, given the six item props, the two action names and the disabled state by this view; the items the product passes are values of that view's props. The view is also built from TanreiFilePicker (FPK-1 to FPK-31) and passes its trigger, its glyph and the nine forwarded props on to it; what this note says of the picker, of accept and of the drop target is that component's, and stays here until this platform is built. The framework has no file picker and no drop target for files; both are plugins, and the package depends on none. TanreiFileUpload(items: …, trigger: TanreiButton(…), onPick: (request) async => …, onSelect: …): onPick is the product's function that opens whatever picker it uses with the request's multiple and accept and returns the files as TanreiPickedFile(name, size, mimeType, handle); the file upload judges them by FUP-9 and emits. Drops arrive the same way: the product's drop plugin calls TanreiFileUploadController.dragEntered / dragExited / dropped(files), and without it the drop-area layout is drawn and pressed and takes no drop — the README says so. disabled is the inverse flag enabled. The announcement is SemanticsService.announce. No form submission. Whether the package should depend on a picker plugin instead is not settled. rows is whether items is passed — null (the default) is own, a list, also an empty one, is given. The TanreiFileUploadController that already takes drops gains files (an unmodifiable view), add(files) and clear(); without a controller the widget makes its own. formatSize: String Function(int bytes)?; the same file is the same handle, or the same name, size and time of last change where the product's picker gives no location. No form submission: inside a Form it is a FormField whose value is the kept files, so that reset() clears and a validator of the product's can ask for one. The rows arranged by the List and the reorderable form (FUP-59) are not built on this platform yet. Each row is a FileItem (FIT-1 to FIT-33), the same view a product places alone, given the six item props, the two action names and the disabled state by this view; the items the product passes are values of that view's props. The view is also built from FilePicker (FPK-1 to FPK-31) and passes its trigger, its glyph and the nine forwarded props on to it; what this note says of the picker, of accept and of the drop target is that component's, and stays here until this platform is built. React Native has no file picker of its own; the package depends on no picker library. <FileUpload items={…} trigger={<Button label="Choose files" />} onPick={async ({ multiple, accept }) => files} onSelect={…} />: onPick is the product's function that opens its picker and returns { name, size, type, uri }[]; the file upload judges them by FUP-9 and emits. No drop of files on phones: the drop-area layout is drawn and pressed and takes no drop, and the README says so. The announcement is AccessibilityInfo.announceForAccessibility. No form submission. Whether the package should depend on a picker plugin instead is not settled. rows is whether items is passed — undefined is own, an array, also an empty one, is given. Own is the uncontrolled form: <FileUpload ref={upload} trigger={…} onPick={…} onChange={(files) => …} />, the ref holding files, add(files) and clear(). formatSize?: (bytes: number) => string; the same file is the same uri. No form. The rows arranged by the List and the reorderable form (FUP-59) are not built on this platform yet.

Tokens

Token Alias de Valor resuelto (claro)
--tanrei-comp-file-upload-list-gap sys.space.space200 16px

Fixtures

Representaciones canónicas. Todas las plataformas capturan cada fixture con el mismo identificador para poder comparar los resultados.

Generado a partir de specs/file-upload.spec.json, tokens/comp/file-upload.tokens.json, design/tokens/

Qué dibuja la web de otra manera

  • El shadow root del elemento contiene un <tanrei-file-picker>, la lista y una región activa. Con rows="own", la lista contiene los <tanrei-file-item> que crea el control de subida; con rows="given", tus elementos de archivo se quedan en tu DOM, en el slot predeterminado. Importar el elemento define los dos miembros.
  • El activador y el glifo que pones en sus slots se pasan al selector de archivos y se quedan en tu DOM. El activador sigue siendo tu propio botón, y recibe el foco allí donde está.
  • El elemento está asociado a formularios, y sus dos miembros no: no añaden ninguna entrada al formulario.
  • select, reject y change son CustomEvents propios del control de subida. Los select y reject del selector de archivos no salen de él. Los click, remove y retry de los elementos de archivo que das tú se propagan hasta tus listeners; los de los elementos que crea el control de subida se responden dentro.
  • Partes: file-picker, list, y file-item por cada elemento que crea el control de subida; las partes container, icon, label, supporting-text y selection-text del selector de archivos, con sus propios nombres; y las partes de un elemento que crea el control de subida, con el prefijo item-: item-container, item-media, item-icon, item-preview, item-headline, item-supporting-text, item-status-icon, item-progress, item-action. Un elemento de archivo que das tú recibe estilo directamente, como el elemento que es: tanrei-file-item::part(icon).
  • Con item-preview y rows="own", cada elemento de archivo crea la dirección de su propia imagen y la libera. El control de subida no crea ninguna.
  • El elemento no puede recibir el foco por sí mismo: cada parada es tu activador o está dentro de un elemento de archivo.

Pautas

La elección y los límites

La forma de elegir es la del selector de archivos: las dos disposiciones button y drop-area, tu propio botón en el slot trigger, un glifo en el slot icon y los límites accept, maxSize y multiple, con constraintsText para decirlos. Su página los explica por completo. Son propiedades del control de subida con los mismos nombres.

Lo que el control de subida añade a los límites:

  • maxFiles es el máximo de archivos de toda la lista, no de una elección. Con rows="own" cuenta los archivos que se conservan; con rows="given", los elementos de archivo que diste. Una elección se juzga según el número de archivos que aún caben.
  • El mismo archivo elegido dos veces se conserva una sola vez, con rows="own".
  • Con rows="own", cada archivo rechazado se dibuja como una fila fallida con tu frase para su motivo: typeErrorText, sizeErrorText o countErrorText. Dalas en el elemento, o una sola vez para todo el producto con setWords().
  • Con rows="given" no se dibuja nada por un rechazo. reject te da cada archivo rechazado con un motivo: type, size o count. Di qué ocurrió con tus propias palabras, con el texto de error del campo, con una fila fallida o con announcement.
  • emptyText es lo que aparece junto al activador, en la disposición de botón, mientras no hay ningún archivo elegido.

Quién tiene las filas

Qué rows="own" (el valor predeterminado) rows="given"
Úsalo para Un formulario que se envía de una vez. Un producto que envía cada archivo por su cuenta.
Las filas Elementos de archivo que crea el control de subida, uno por cada archivo conservado y por cada archivo rechazado. Hijos <tanrei-file-item> que añades y quitas tú.
Una segunda elección Se suma con multiple; sustituye sin él. El mismo archivo se conserva una sola vez. Lo decides tú.
Un archivo rechazado Una fila fallida con tu frase. Solo reject.
Una fila muestra Nombre, tamaño, quitar. Ni estado ni progreso: no se está enviando nada. Nombre, tamaño, estado, progreso, error, reintentar, quitar.
Quitar Quita la fila y emite change. Emite el remove de la fila. La quitas tú.
El valor del formulario Los archivos conservados. El file de cada fila a la que diste uno.
Un restablecimiento del formulario Vacía la lista. No cambia nada.

El control de subida nunca deduce el modo a partir de sus hijos. Un producto que es dueño de sus filas no tiene ninguna antes de la primera elección, así que dice rows="given".

Cambiar rows mientras el control de subida se muestra descarta todos los archivos conservados sin emitir ningún evento. Lee antes files si los necesitas.

Filas o mosaicos

itemForm elige la forma de todos los archivos: row, la predeterminada, una columna de filas; o tile, una tira de recuadros que salta de línea. Todos los archivos de un control de subida se dibujan con la misma forma.

Propiedad En cada elemento de archivo Qué hace
itemForm form row o tile.
itemPreview preview Dibuja una imagen en el lugar del glifo: la de la dirección previewSrc del elemento, o la del propio archivo cuando es una imagen.
itemFit fit cover o contain.
itemNameHidden nameHidden En un mosaico que dibuja una imagen, el nombre no se dibuja.
itemWidth, itemHeight width, height El recuadro de un mosaico.

Con rows="own" e itemPreview, un archivo conservado que es una imagen muestra su propia imagen. Cada imagen tiene un coste, que es el del elemento de archivo y se explica en su página. Sin itemPreview, el valor predeterminado, cien archivos conservados cuestan cien referencias y nada más.

Archivos en un orden que las personas cambian

Con reorderable, las personas pueden poner los archivos en otro orden. Cada fila recibe un asa, y la fila entera se puede arrastrar —con el ratón tras unos píxeles, con el dedo tras una pulsación larga o enseguida desde el asa— mientras las demás filas se apartan. El asa también es un botón: abre un menú con cuatro movimientos, y con el teclado Espacio levanta la fila, las flechas la llevan, Espacio la suelta y Escape la devuelve. Arrastrar nunca es la única forma (WCAG 2.5.7), y cada movimiento se anuncia con tus palabras.

Con rows="own", el control de subida reordena él mismo los archivos que conserva, los envía en el nuevo orden y emite change. Con rows="given" emite move con from y to y no mueve nada: pon tu elemento de archivo en su nuevo lugar.

Las palabras son las de la lista: drag-handle-label (Reordenar {label}, con el nombre del archivo), las cuatro palabras de movimiento y las tres frases.

Un control de subida reordenable que conserva sus propios archivos. Elige algunos archivos y arrastra una fila, o usa el menú de su asa o el teclado. No se envía nada.

Elegir archivos
<tanrei-file-upload name="attachments" multiple reorderable remove-label="Quitar {name}"
  drag-handle-label="Reordenar {label}" move-to-start-label="Mover al principio" move-before-label="Subir"
  move-after-label="Bajar" move-to-end-label="Mover al final"
  lifted-text="{label} levantado: {position} de {count}" moved-text="{label}: {position} de {count}"
  cancelled-text="{label} devuelto a {position} de {count}">
  <tanrei-button slot="trigger" variant="outlined">Elegir archivos</tanrei-button>
</tanrei-file-upload>

Las filas que das tú

Con rows="given", cada fila es un elemento de archivo que escribes tú, con sus propias propiedades: value, name, mediaType, formatText, sizeText, status, statusText, progress, errorText, retryable, previewSrc, interactive, href y readOnly. Su página dice qué dibuja cada una. Una fila que el control de subida crea él mismo es un elemento de archivo pending para un archivo conservado, y uno error sin reintento para un archivo rechazado.

status Antes del nombre Segunda línea Barra Acciones
pending El glifo del formato El tamaño y después statusText Ninguna Quitar
uploading El glifo del formato El tamaño y después statusText Llena hasta progress, o indeterminada mientras progress no está definido Quitar
done El glifo del formato El tamaño y después una marca de verificación y statusText Ninguna Quitar
error El glifo del formato El tamaño y después una marca de error y errorText Ninguna Reintentar donde retryable, y después quitar
  • Da tú formato al tamaño y dalo como sizeText, o como la función formatSize con rows="own". Si un megabyte son 1000 o 1024 kilobytes es decisión de tu producto.
  • progress es tu informe de tu propio envío. El componente nunca mueve la barra por sí mismo.
  • No definas en una fila lo que el control de subida da a todas las filas: su forma, su vista previa, los dos nombres de acción, disabled.

En un campo

Pon el control de subida en un campo cuando el formulario muestre una etiqueta, una ayuda, un error o una marca de obligatorio. El campo los dibuja y se los da al control de subida.

<tanrei-field label="Archivo adjunto" required required-text="Obligatorio"
              error error-text="Elige un PDF de 10 MB como máximo.">
  <tanrei-file-upload name="attachment" accept=".pdf" max-size="10000000"
                      constraints-text="PDF, hasta 10 MB" remove-label="Quitar {name}">
    <tanrei-button slot="trigger" variant="outlined">Elegir un archivo</tanrei-button>
  </tanrei-file-upload>
</tanrei-field>
  • Con un name, el control de subida envía todos los archivos conservados, en orden. Un archivo rechazado nunca se envía. Mientras no se conserva ningún archivo no envía ninguna entrada; un campo de entrada de archivos nativo envía una entrada vacía, así que un servidor que lee el campo comprueba ambos casos.
  • required notifica al formulario que falta un valor mientras no se conserva ningún archivo. El mensaje es el del propio navegador. La frase que lee una persona es el errorText del campo. Un botón no tiene estado obligatorio, así que pon un control de subida obligatorio en un campo: el campo lo dice antes de que el formulario se envíe.
  • Un restablecimiento del formulario vacía la lista. Después de un envío, los archivos y las filas se quedan; restablece el formulario o llama a clear().
  • No se restaura nada cuando se vuelve a una página por el historial. El control de subida empieza vacío.
  • Sin un name, el control de subida no envía nada y, aun así, conserva sus archivos y los muestra en la lista. Lee files en tu propio controlador de envío para enviarlos por tu cuenta.
  • Con rows="given", el valor es el file de cada fila que tiene uno: row.file = file. Un restablecimiento no cambia nada ahí, porque las filas son tuyas.
  • El control de subida nunca define error por sí mismo. Lo defines tú, en el campo, con las palabras que dicen qué está mal.

Textos

El control de subida no incluye textos propios. Seis se pueden dar una sola vez para todo el producto con setWords(); todo lo demás es una propiedad de la instancia.

Propiedad Clave de los textos del producto Sin ella
emptyText noFileText No se dibuja nada junto al activador.
removeLabel removeLabel La acción de quitar se dibuja y no tiene nombre. Una compilación de depuración lo notifica.
retryLabel retryLabel La acción de reintentar se dibuja y no tiene nombre. Una compilación de depuración lo notifica. Solo la tienen las filas que das tú.
typeErrorText fileTypeErrorText Un archivo rechazado por su tipo se dibuja como una fila fallida sin frase. Una compilación de depuración lo notifica.
sizeErrorText fileSizeErrorText Lo mismo, para un archivo demasiado grande.
countErrorText fileCountErrorText Lo mismo, para un archivo que ya no cabe.

Las tres frases solo se leen con rows="own", y solo una vez que se ha rechazado un archivo por ese motivo. El tamaño de un archivo no es una clave de texto: es la función formatSize, una propiedad de la instancia.

El nombre de cada acción lleva el nombre de su archivo: removeLabel y retryLabel son frases con el marcador {name}, que el elemento de archivo rellena («Quitar {name}»). Una frase basta para todos los archivos, y una persona oye «Quitar brief.pdf, botón» —en el orden de tu idioma—, también cuando un lector de pantalla enumera los botones de la página. Una frase sin {name} da el mismo nombre a la acción de cada archivo, y una compilación de depuración lo notifica.

De derecha a izquierda

En un diseño de derecha a izquierda, el activador y los glifos de las filas están a la derecha, las acciones de las filas a la izquierda, con la de quitar en el extremo, y una barra se llena desde la derecha; una tira de mosaicos empieza por la derecha. Un nombre de archivo en letras latinas conserva su propio orden, con su punto y su extensión. Un glifo solo se refleja donde el conjunto de iconos lo marca, y una imagen nunca se refleja.

Recomendado

  • Coloca el control de subida en el formulario y deja que conserve los archivos, salvo que tu producto envíe cada archivo por su cuenta.
  • Mantén la lista de filas corta y en el orden en que las personas eligieron los archivos.
  • Di un rechazo donde la persona está mirando: en el texto de error del campo o en una fila fallida.
  • Resume muchos cambios en una sola frase con announcement: «Se añadieron 40 archivos».

Evita

  • No uses la zona para soltar como la única forma de elegir. Siempre contiene el activador.
  • No pongas dentro nada que no sea el activador, un icono y elementos de archivo. Cualquier otro contenido no se dibuja, y una compilación de depuración lo notifica.
  • No escribas filas sin rows="given". No se dibujan.
  • No uses rows="own" para mostrar el progreso de una subida. La fila de un archivo conservado no tiene estado, porque el control de subida no envía nada.
  • No muestres dentro un recuento, un tamaño total ni una barra global. Dibújalos al lado, con tus propias palabras.

Accesibilidad

Rol
group
Etiquetado
A group named by label, or by the label of the Field around it, holding the file picker and the list. The file picker is given no name and adds no group; its trigger is a button named by its own label and described by constraintsText, by emptyText while nothing is chosen, and by the Field's help and, in error, its error text, and is exposed as required, invalid and disabled as the props say (FPK-17). The rows are a list, and each file item an item of it: it reads the file's name, its format, its size, its status or its error in words, a progress bar named by the file while it is sent, and its actions — buttons named retryLabel and removeLabel, each with the file's name in it (FIT-19).
Objetivo mínimo
El área de pulsación cumple el tamaño mínimo de objetivo aunque el tamaño visual sea menor. minimum 32px · pointer 44px · spacing 8px · touch 48px

Teclado

Teclas Acción
Tab / Shift+Tab Moves focus to the trigger, then to each row's stops in the order of the rows — the body of a row that is pressable or a link, retry where a row has it, then remove. The drop area is not a stop, and a row that cannot be pressed is none.
Enter / Space On the trigger: opens the platform's file picker, as the Button's own activation. On a row's action: removes that file, where the file upload keeps its own; asks the product to retry or to remove it, where the product gives the rows. On a row's body that is pressable: the product opens the file (the file item's click).
The picker's own keys Inside the platform's picker everything is the platform's; leaving it without choosing returns focus to the trigger and changes nothing.

Notas

  • Choosing is the file picker's, and so is everything a person meets while choosing: that dragging is never the only way, that the limits are words before the choice, that a drag over the area is not announced and is told without color (the file picker's notes; FPK-5, FPK-12, FPK-13, FPK-18, FPK-23). A file is shown by a file item, and everything a person meets on one file is the file item's: that its status is never color alone, that its name is its name in every form, that a failure is never hidden, how it is named and described (the file item's notes; FIT-4, FIT-8, FIT-19). This spec says what the file upload adds: the arrangement, which files there are, the one live region, where focus goes, the form.
  • Without script, and until the component is defined, on the web: the trigger is a button that opens nothing, no row is drawn and no file is submitted. A custom element cannot open the platform's picker or carry a file by markup alone, and the file upload does not pretend to: a page that must take files where script does not run writes a native file input in its own form, as it would without Tanrei, and loses only the drawing.
  • One live region, polite (FUP-26). A row says its own name and status when it is added, finishes or fails, in the product's words; many changes in one turn are one message, and the product can replace them with one sentence of its own (announcement). Progress is never announced: a person reads it on the row's progress bar when they go there.
  • A refused file must be said (WCAG 3.3.1), and only the product has the words. While rows is given, on reject it sets the Field's error text, adds a failed row with errorText, or sets announcement, and the file upload draws and says nothing for a refusal by itself, because it has no sentence for it. While rows is own the file upload draws a row for each refused file, with the error glyph, the file's name and the product's sentence for the reason (typeErrorText, sizeErrorText, countErrorText, or the product's words), and announces the name and the sentence (FUP-45, FUP-50); a product that gave no sentence is told so in debug builds.
  • Status is never color alone (WCAG 1.4.1): a done or failed row has words and a mark with a shape of its own, before those words (FIT-4). The glyph before the name says the file's format in every status.
  • Focus is never lost on removal (FUP-30): it goes to the next row's remove action, the previous one's, or the trigger.
  • The remove and retry actions are named once for the whole list and described by each file's name, so a person tabbing through them hears "Remove, button, brief.pdf" without the product writing a sentence per row.
  • A file's name is never cut in a column of rows (FIT-2): two names that differ only at the end must be told apart, and the extension is at the end. In a strip of tiles it is one line, cut in the middle so that its ending stays (FIT-9), and whole for assistive technology.
  • Compose and React Native have no list role for an arbitrary layout: the rows are a container with collection information (Compose) or a labelled container (React Native), and each row keeps its parts.
  • While the file upload keeps its own files (rows own, FUP-42 to FUP-55) a person hears what was added — each file's name, once, politely, in one message for one choice — and nothing of the file upload's own invention: no count and no word such as "added", which Tanrei does not have. A product that wants a sentence sets announcement in its answer to change, and that sentence is said alone.
  • A removal is told by where focus goes, not by a message (FUP-46, FUP-50): the next row's remove action is described by that file's name, and once the last row is gone focus is on the trigger, whose description holds emptyText ("No file chosen"). A form reset and a clearing by the product move focus the same way and announce nothing; a product that clears the list for a reason a person must hear says it with announcement.
  • A plain form's missing file is the form's own validation (FUP-49): the message is the platform's own, as for every Tanrei control, and stands at the trigger, which takes focus as an invalid control does; the product's own sentence is the errorText of the Field around the file upload, which is what is drawn and described there. The file upload sets no error condition itself, for a missing value or for a refused file (FUP-16). Before a submission, that a file is required is said only inside a Field, whose required text joins the trigger's description: where the trigger is a button the platform gives it no required state (the web), and the file upload has no word for it. A form that must say it frames the file upload in a Field; placed alone, with required, it works and reports the missing file only when the form validates.
  • The Field's fixture file-upload-required-error (the Field's spec) holds a file upload with no rows prop, so it is one that keeps its own files, with none kept: the trigger and the line of limits, no list and no emptyText, since none is given; the label, the required mark and the error text are the Field's, and the trigger is exposed as invalid and described by that error text (FUP-32). It is drawn as it was before rows existed. A missing file would be a missing value there (FUP-49), and what a person reads for it is the Field's errorText.
  • A fixture whose rows is own and that holds file items cannot be drawn from props alone, because a file is no value a fixture holds: each file item stands for one file the stage hands over, in order, by the platform's way of FUP-48, before anything is recorded — its name is the file's name, and its sizeText is what the stage's size function answers for that file; no other prop of the item is read, and whether the file is kept or refused is the component's own judging of the fixture's accept, multiple and maxFiles (FUP-43). That is the mark, and the only one: the fixture format has no field for a step after the component exists, so every harness, gallery and documentation renderer reads rows and the items. A fixture whose rows is given draws its items as the product's file items, as before. A file the stage hands over is empty, so no picture of it can be drawn: a staged row of an image shows the image glyph also where itemPreview is true (FIT-6), and a picture in a fixture comes from a row's previewSrc, which only a fixture whose rows is given has.
  • One level down: a file picker, and file items the product arranges. The product hears the file picker's select and reject, keeps the files, places a file item for each — in a column, in a strip, or each as the content of an item of a reorderable List — judges the room itself or sets the file picker's maxFiles to it, says what was added and refused through a live region of its own, moves focus when it removes one, and sends the files or gives them to a form control of its own. What it does not get by hand, because it is the state the file upload holds: the one message for many changes (FUP-26); the focus that goes on after a removal (FUP-30); the form's value (FUP-31, FUP-49); and that no file is kept twice (FUP-43). The picture is the same: a file item is the same element alone and inside a file upload.
  • A strip of tiles (itemForm tile) is still one list of items in the order of the files, read and tabbed through line by line in the reading direction; wrapping changes no order. A tile whose name is hidden is named by that name (FIT-8), and its error text is drawn beneath it.
  • Files in an order people change are the file upload's own reorderable form (FUP-59): its List gives every row a handle, lets the whole row be dragged, and offers the four moves in a menu and the keyboard from the handle, so that dragging is never the only way (WCAG 2.5.7) and the keyboard can do it (WCAG 2.1.1); where the row now stands is announced by the List in the product's words.

Generado a partir de specs/file-upload.spec.json, tokens/sys/measure.tokens.json

De qué se encarga el componente

  • El conjunto es un grupo que recibe su nombre de label, o del campo que lo rodea. El activador es tu botón. Lo describen los límites, el texto que dice que no hay nada elegido y la ayuda o el error del campo.
  • La zona para soltar es un único objetivo del puntero, grande, para ese mismo botón. No tiene rol, ni nombre, ni parada en el orden del foco, ni tecla.
  • Las filas son una lista, y cada elemento de archivo es un elemento de ella. De cada uno se leen el nombre, el formato, el tamaño y el estado o el error. Un archivo que se está enviando tiene una barra de progreso que recibe su nombre de su archivo.
  • Tab llega al activador y después a cada archivo, en orden: su cuerpo donde lo hiciste pulsable, después reintentar y después quitar.
  • Una única región activa dice, de forma cortés y con tus palabras, una fila que se añadió, una fila que terminó y una fila que falló. Los cambios que llegan juntos son un solo mensaje. El progreso nunca se anuncia.
  • Con rows="own", una elección es un mensaje: el nombre de cada archivo que se conservó y después el nombre de cada archivo rechazado con tu frase. El control de subida no añade ninguna palabra ni ningún recuento. Define announcement en tu listener de change para decir en su lugar una frase tuya.
  • Una fila que se quita no se anuncia. Después de quitar una fila, el foco va a la acción de quitar de la fila siguiente, a la de la anterior donde no sigue ninguna, o al activador cuando no queda ninguna fila. Con rows="own", el control de subida lo hace él mismo.
  • Con el texto al 200%, todos los textos saltan de línea y nada se corta. En colores forzados, la zona conserva un borde, una zona que está recibiendo un arrastre tiene uno más grueso y la barra de una fila sigue siendo visible.

Cómo está construido en la web: el selector de archivos es un miembro del control de subida, en su shadow root, y el selector que hay dentro de él es un <input type="file"> real, apartado de la vista, del orden del foco y de la tecnología de apoyo. El activador se queda en tu DOM, y también los elementos de archivo que das tú. El grupo, la lista, la región activa y los elementos de archivo que el control de subida crea él mismo están en el shadow root.

Las carencias conocidas en la web son las de sus miembros: un botón no tiene estado obligatorio, un navegador no entrega lo que se suelta en una zona que rechaza el arrastre, Safari no da el foco a un botón en el que se hace clic, y un borde discontinuo no puede seguir unas esquinas suavizadas (la página del selector de archivos); una imagen que se mueve la reproduce el navegador (la página del elemento de archivo). Con required solo y sin un campo alrededor del control de subida, únicamente la validación del formulario dice que falta un archivo.

Qué debes hacer tú

  • Pon un botón en el slot trigger, con una etiqueta que diga qué hace.
  • Da al grupo un label, o pon el control de subida en un campo.
  • Da constraintsText siempre que definas accept, maxSize o maxFiles.
  • Da removeLabel, en el elemento o una sola vez con setWords(). Da también retryLabel cuando des filas que se puedan reintentar.
  • Con rows="own", da las tres frases para un archivo rechazado: typeErrorText, sizeErrorText y countErrorText, para los límites que definas.
  • Di con palabras cada rechazo y cada fallo.
  • Define announcement cuando muchas filas cambien a la vez, y cuando quites filas de una manera que una persona deba oír.