跳到主要内容
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
开始使用WebReactSwiftUIJetpack ComposeFlutterReact Native预设你自己的设计系统与 AI 代理一起使用 Tanrei品牌设计令牌设计系统与定制Theme Studio颜色字体排印图标形状层级动效交互状态行为布局无障碍文字颜色令牌字阶令牌形状令牌层级令牌动效令牌状态令牌间距与度量令牌组件按钮主操作按钮分段按钮按钮组带菜单的按钮工具栏复制按钮复选框单选按钮开关纸片(Chip)菜单滑块滑块字段日历DateTimePicker字段组单选组复选框组下拉选择框下拉选择字段菜单栏文件上传文件选择器 进阶放置区文本输入框 进阶文本字段纸片字段字段组合框 进阶组合框字段数字输入框 进阶数字字段颜色输入框 进阶颜色字段一次性验证码输入框 进阶一次性验证码字段嵌入式文本输入框 进阶聊天输入栏卡片分隔线模态层 进阶对话框列表列表行底部面板侧边面板轮播代码块表格弹出框步骤折叠面板描述列表图示应用外壳页头树视图时间线图片预览调整手柄可调整大小的框滚动区域徽标进度指示器计量条数字滚动器加载指示器消息条(Snackbar)工具提示键盘快捷键标签(Tag)行内代码提示块(Callout)头像空状态标签组(Tag group)骨架屏文件项色块跑马灯文本标签页标签页视图应用栏导航栏导航抽屉搜索链接分页上一页/下一页链接步骤条面包屑目录链接卡片FAB 菜单状态Tanrei 是如何构建的参与贡献许可与致谢
首页组件文件项

文件项

显示一个文件,包括表示格式的字形或文件的小图、文件名,以及产品对它的说明,并带有移除和重试两个操作;它可以是一列文件中的一行,也可以是一排图片中的一个图块。可以按下它来打开文件。它自己绝不读取、保留或发送文件。

content_copy以 Markdown 格式查看
类别
信息传达
令牌
comp.file-item
层级
基础

概览

Web
tanrei-file-item
schedule
SwiftUI
TanreiFileItem
radio_button_unchecked
Jetpack Compose
TanreiFileItem
radio_button_unchecked
Flutter
TanreiFileItem
radio_button_unchecked
React Native
FileItem
radio_button_unchecked

生成自 docs/status/status.json, specs/file-item.spec.json

一个文件显示为行,几个文件显示为图块

文件项就是把一个文件显示出来。文件名前面是一个表示文件格式的字形,或者是文件的一张小图。文件名下面有一行,写的是你的产品对它的说明:格式、大小、发送进行到哪里,或者出了什么问题。末尾是文件都有的两个操作:移除它,以及重试。

它有两种形式:

  • row,默认值:一行,宽度由它所在的位置决定。用于一列文件。
  • tile:一个方框,宽度和高度由你给出,名称在方框下方。用于一排图片。

它只负责显示,别的什么都不做

文件项不读取文件的任何一个字节,不保留任何文件,也不发送任何东西。它也不移除任何东西:它的移除操作只是告诉你用户提出了这个要求,由你把这个文件项拿掉。它显示的内容就是它的属性所给出的内容。

使用它的组件是文件上传,那里每个已选的文件都是一个文件项。只要你的产品要显示它已经持有的文件,就可以单独使用文件项:消息的附件、一条记录下的文档、已经发出的请求里的文件。

以下情况请使用别的组件:

  • 用户选择文件,由你把它们列出来。那是文件上传,它替你持有文件项。
  • 用户要给文件排顺序。那是各项包含文件项、可调整顺序的列表;参见顺序可由用户调整的文件。
  • 用户要在文件中选择。那是带选择功能的列表。文件项没有选中状态。
  • 图片本身就是供人观看的内容。文件项的图片占的是字形的位置,没有替代文本。

在 Web 上它是 <tanrei-file-item>。其他平台尚未实现。

用法

给出 name,以及你手头有的各项文本。value 是 click、remove 和 retry 交还给你的值。

<ul class="attachments" aria-label="附件">
  <li>
    <tanrei-file-item value="a" name="brief.pdf" format-text="PDF" size-text="820 KB"
                      href="/files/brief.pdf" remove-label="移除 {name}"></tanrei-file-item>
  </li>
  <li>
    <tanrei-file-item value="b" name="floor-plan.png" size-text="3.4 MB"
                      status="uploading" status-text="正在上传" progress="0.4"
                      remove-label="移除 {name}" retry-label="重试 {name}"></tanrei-file-item>
  </li>
</ul>

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

  const list = document.querySelector('.attachments');
  // 文件项绝不会自己移除自己。由你把它拿掉、停止发送这个文件,并移动焦点。
  list.addEventListener('remove', (event) => {
    cancel(event.detail.value);
    event.target.closest('li').remove();
  });
  list.addEventListener('retry', (event) => send(event.detail.value));
</script>
import { FileItem } from '@tanrei/react';

<ul aria-label="附件">
  {files.map((file) => (
    <li key={file.id}>
      <FileItem value={file.id} name={file.name} sizeText={file.sizeText}
        status={file.status} statusText={file.statusText} progress={file.progress}
        errorText={file.errorText} retryable={file.retryable}
        removeLabel="移除 {name}" retryLabel="重试 {name}"
        onRemove={() => remove(file.id)} onRetry={() => retry(file.id)} />
    </li>
  ))}
</ul>

removeLabel 和 retryLabel 可以用 setWords() 为整个产品只给一次。其余的文本都属于各个实例。

规格

每种状态一行:等待中、已发送 40%、进行中、已完成、失败但可重试、彻底失败

构成

构成

绘制在 Web 包所绘的测试样例 row 之上。
部件 说明
1 container The body: the glyph or the picture, and the texts. One part, and the one that is pressed where the file item is pressable. The actions stand beside it, never inside it.
2 media The box of the glyph or the picture: the leading box of a row, the whole box of a tile.
3 icon · 可选 The glyph of the file's format, by its name in the system's icon set; or the product's own glyph. Hidden from assistive technology.
4 preview · 可选 The picture of an image file, in the glyph's place.
5 headline · 可选 The file's name.
6 supporting-text · 可选 The second line: the format, the size and the status in words, or the error text in the status's place.
7 status-icon · 可选 The mark before the words of a file that is done or failed.
8 progress · 可选 A thin bar while the file is being sent: how far it has come, or that it is under way.
9 action · 可选 The actions: retry (while it failed and can be tried again) and remove. Icon-only, each with its own state layer and focus ring.
10 focus-ring The system's focus indicator around the body or an action that has keyboard focus.

属性

属性 类型 默认值 说明
form 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.
value string "" What identifies the file to the product: given back by click, remove and retry.
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).
preview 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.
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.
fit 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.
nameHidden 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.
width 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.
height 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.
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.
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.
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.
disabled boolean false The actions are disabled and the body cannot be pressed. What the file item shows of the file stays as it is and stays readable.
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.

插槽

插槽 说明
icon A glyph of the product's own in place of the format's glyph, where a name in the system's icon set (glyph) is not enough. Decorative.

事件

事件 说明
click The body of a pressable file item was activated by pointer, touch, keyboard or assistive technology. Carries value. A link emits it before its destination opens; a product that takes the navigation itself cancels it.
remove The person asked to take this file back, by its remove action. Carries value. The file item removes nothing: whoever holds it removes it — and stops sending the file, if it was — and moves focus.
retry The person asked to try a failed file again, by its retry action. Carries value. The product sends the file again and sets status.

状态

行为

以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。

ID 需求
FIT-1 THE file item SHALL show one file in the form that form names: a row (FIT-2) or a tile (FIT-3). In both it SHALL draw the same parts from the same props — the glyph of the file's format or a picture of the file (FIT-5, FIT-6), its name, a second line (FIT-4), a bar while it is sent (FIT-10) and its actions (FIT-11) — so that a form changes where the parts stand and how large they are, never what is known of the file. THE file item SHALL draw no container, fill or edge around the whole, SHALL hold one file and never a list of files, and SHALL NOT sort, group, number or count itself among others: how file items are arranged is the layout's, or the component's that holds them.
FIT-2 WHILE form is row THE file item SHALL be as wide as its layout gives it and as tall as its parts, and SHALL lay out from its start edge: the glyph in a box of comp.file-item.row.icon.size — or, WHILE preview is true, a square of comp.file-item.row.media.height with corners of comp.file-item.row.media.shape and the ground comp.file-item.media.color, holding the picture, or the glyph centred where no picture is drawn (FIT-6); comp.file-item.row.container.gap after it the texts, taking the room that is left; and comp.file-item.row.container.gap after them the actions (FIT-11), with the glyph or the square and the actions centred on the row's first two lines. The first line SHALL be name in comp.file-item.headline.typescale and comp.file-item.headline.color, drawn whole: it SHALL wrap, breaking inside a word where it must, and SHALL never be cut with an ellipsis, because the end of a file's name says what kind of file it is and a row has the room. WHILE form is row THE file item SHALL NOT read width, height or nameHidden, and in debug builds SHALL report nameHidden.
FIT-3 WHILE form is tile THE file item SHALL draw a box height high — comp.file-item.tile.media.height WHILE height is null — and width wide, as wide as it is high WHILE width is null, with corners of comp.file-item.tile.container.shape and the ground comp.file-item.media.color, holding the picture clipped to those corners (FIT-6) or, where no picture is drawn, the glyph centred at comp.file-item.tile.icon.size; and beneath the box, comp.file-item.tile.container.gap from it and as wide as it, the name on one line (FIT-9) and the second line (FIT-4). The file item SHALL be exactly as wide as the box. It SHALL draw the actions inside the box at its top end corner, comp.file-item.tile.action.inset from the top and from the end edge, the retry action before the remove action (FIT-11), each on a disc of the tile's action container color so that it is read over any picture; and SHALL draw the box no narrower than both actions, the space between them and the inset on each side need, and no lower than one action and the inset above and below it, in debug builds reporting a width or a height that is less. WHERE its layout gives the tile less room than width THE file item SHALL draw the box as wide as that room and as high as keeps the proportion of width to height, and SHALL never be wider than its layout.
FIT-4 THE file item SHALL draw below the name one line in comp.file-item.supporting-text.typescale that wraps and is never cut: formatText, sizeText and statusText, each WHILE it is not empty, in that order, with a middle dot and a space on each side between two that are drawn, in comp.file-item.supporting-text.color; and WHILE status is error and errorText is not empty, errorText in statusText's place, in comp.file-item.error.supporting-text.color. WHILE status is done or error THE file item SHALL draw before the words of the status or the error, comp.file-item.status.icon.gap from them, a mark at comp.file-item.status.icon.size by its name in the system's icon set and through the platform's icon helper — the checked-circle glyph in comp.file-item.done.icon.color WHILE status is done, the error glyph in comp.file-item.error.icon.color WHILE status is error — and SHALL keep the mark from assistive technology, which is told the status by the words. WHILE the line would hold nothing THE file item SHALL draw none and keep no room for it. A status SHALL never be told by the mark or its color alone: WHILE status is done or error and the product gives no words for it (statusText, errorText) THE file item SHALL still render, with the mark alone on the line, and in debug builds report it. THE file item SHALL draw the second line in every form and whatever nameHidden says: what went wrong is never hidden.
FIT-5 THE file item SHALL choose the glyph from the file's format, in one of ten kinds, by mediaType — read without regard to case, without anything after a semicolon and without the white space around it — and, WHERE mediaType is empty, is application/octet-stream or is on no list below, by the end of name after its last dot, without regard to case, the first kind that matches in this order, each kind by a closed list of media types and a closed list of endings: pdf (application/pdf; pdf); spreadsheet (text/csv, text/tab-separated-values, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.oasis.opendocument.spreadsheet, application/vnd.apple.numbers, application/x-iwork-numbers-sffnumbers; xls, xlsx, ods, numbers, csv, tsv); presentation (application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.oasis.opendocument.presentation, application/vnd.apple.keynote, application/x-iwork-keynote-sffkey; ppt, pptx, odp, key); archive (application/zip, application/x-zip-compressed, application/x-tar, application/gzip, application/x-gzip, application/x-bzip2, application/x-xz, application/x-7z-compressed, application/vnd.rar, application/x-rar-compressed; zip, tar, gz, tgz, bz2, xz, 7z, rar); code (text/html, text/css, text/javascript, application/javascript, application/x-javascript, text/typescript, application/typescript, text/jsx, text/tsx, application/json, application/xml, text/xml, text/yaml, text/x-yaml, application/yaml, application/x-yaml, application/x-sh, text/x-sh, text/x-shellscript, text/x-python, text/x-script.python, text/x-ruby, application/x-ruby, text/x-go, text/x-rust, text/x-java, text/x-java-source, text/x-kotlin, text/x-swift, text/x-c, text/x-chdr, text/x-csrc, text/x-c++, text/x-c++src, text/x-csharp, text/x-php, application/x-php, application/x-httpd-php, application/sql, text/x-sql; html, css, js, mjs, ts, tsx, jsx, json, xml, yaml, yml, sh, py, rb, go, rs, java, kt, swift, c, h, cpp, cs, php, sql); image (every media type that begins with image/; png, jpg, jpeg, gif, webp, avif, svg, bmp, tif, tiff, heic, heif, ico); video (every media type that begins with video/; mp4, mov, m4v, webm, mkv, avi); audio (every media type that begins with audio/; mp3, wav, m4a, aac, ogg, oga, flac, opus); document (every media type that begins with text/ and is on no list above, application/rtf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.oasis.opendocument.text, application/vnd.apple.pages, application/x-iwork-pages-sffpages; txt, md, rtf, doc, docx, odt, pages); and other for everything else, a name without a dot among them. A media type that is on no list SHALL choose nothing by itself: the ending decides, and then other. video/mp2t SHALL choose nothing by itself either, though it begins with video/: platforms guess it from the ending .ts, which TypeScript files share with MPEG transport streams, so the media type is no surer than the ending and the ending decides. The lists are the whole of the rule on every platform, so that one file draws one glyph wherever it is shown; a platform SHALL add no media type and no ending of its own. It SHALL draw, by its name in the system's icon set and through the platform's icon helper: picture_as_pdf, table, slideshow, folder_zip, code, image, movie, music_note, description and draft, in the order of the kinds. WHILE glyph is not empty THE file item SHALL draw that glyph in the format's place, and WHILE the icon slot holds a glyph SHALL draw the slot's in the place of both. THE file item SHALL draw every such glyph in comp.file-item.icon.color, whatever the format — a format has no color of its own, because a color says one thing wherever it is used and a format is not a status — and SHALL keep it from assistive technology: the name says which file it is, and formatText says its format in words where the product has them. THE file item SHALL NOT open the file to learn its format, and SHALL NOT change the glyph because of status.
FIT-6 WHILE preview is true THE file item SHALL draw a picture of the file in the glyph's place: the picture at previewSrc WHILE previewSrc is not empty, whatever the file's format; otherwise, WHILE the file's kind is image (FIT-5) and the product gave the file item the file itself in the platform's idiom, the platform's own drawing of that file (FIT-7). It SHALL fill the box as fit names — cover: scaled to fill the box and centred, what does not fit cut at the box's edges; contain: scaled to stand whole inside the box, centred on the box's ground — never stretched out of its proportion and never drawn larger than the box. Until the picture can be drawn, and IF it cannot be drawn — an address that does not answer, a format the platform does not draw, a file that is not a picture after all — THE file item SHALL draw the glyph of the file's format in its place, SHALL draw no sign of loading and no sign of the failure, SHALL NOT set status and SHALL emit nothing for it, and in debug builds SHALL report a previewSrc that could not be drawn. WHEN the picture can be drawn THE file item SHALL draw it at once in the glyph's place, without a motion and without changing the size of anything. THE file item SHALL keep the picture from assistive technology: it stands where the glyph stood, and the file is named by name. WHILE preview is false, WHILE preview is true and neither an address nor a file was given, and for a file that is not an image and has no previewSrc, THE file item SHALL draw the glyph. THE file item SHALL draw no picture of a video, of a page of a document or of anything else it would have to open and read to draw.
FIT-7 THE file item SHALL NOT read a file: it SHALL open no connection for a file, SHALL NOT read a file's bytes into memory of its own, SHALL NOT copy, store, resize or encode a file, and SHALL make of the file the product gave it nothing but what the platform needs to draw the picture of FIT-6. WHERE the platform draws a picture of a file from an address it makes for the file's handle (the web's object URL) THE file item SHALL make that address itself, at most one per file item; SHALL make it only WHILE FIT-6 has it draw the picture from the file — preview is true, previewSrc is empty, the kind is image — and only once the file item is in or near what is in view; SHALL give it to nothing but its own picture; and SHALL release it WHEN the file item is taken out of the page or the screen, WHEN the product gives it another file or none, and WHEN preview becomes false, previewSrc is given or the kind is no longer image. WHERE the platform makes a small picture of a file itself (a thumbnail the system gives for a handle) THE file item SHALL ask for one no larger than its box and SHALL cancel the request on the same occasions. A file item whose preview is false SHALL cost what its texts cost: no address, no picture and no request is made for it, however many there are.
FIT-8 WHILE form is tile and nameHidden is true and the box draws a picture THE file item SHALL NOT draw the name beneath the box and SHALL keep no room for it. WHILE the box draws no picture — preview is false, the picture is not drawn yet or cannot be drawn — THE file item SHALL draw the name whatever nameHidden says, because a glyph of a format does not tell two files of that format apart. In every case name SHALL stay the file item's name for assistive technology (FIT-19), and the second line SHALL be drawn (FIT-4). IF name is empty THE file item SHALL still render and in debug builds report that the file has no name.
FIT-9 WHILE form is tile THE file item SHALL draw the name on one line in comp.file-item.headline.typescale and comp.file-item.headline.color, no wider than the box. WHERE the name does not fit THE file item SHALL cut it in the middle and never at its end: the ending of the name — from its last dot, WHERE that dot is not the name's first character and is among its last eight — SHALL stay drawn, the start of the name SHALL be drawn as far as the room that is left allows, and an ellipsis SHALL stand between the two, so that two pictures from one camera are still told from a document of the same name; a name with no such ending SHALL be cut at its end, with an ellipsis. The ellipsis is a mark of the platform's text layout and not a word. The whole name SHALL reach assistive technology, and the cut SHALL be made in the drawing alone: THE file item SHALL NOT change name. The second line of a tile SHALL wrap inside the box's width and SHALL never be cut (FIT-4).
FIT-10 WHILE status is uploading THE file item SHALL draw a bar comp.file-item.progress.thickness high: a track in comp.file-item.progress.track.color and over it, from the track's start, an indicator in comp.file-item.progress.indicator.color across the fraction progress gives, clamped to 0 and 1, moving to a new value over comp.file-item.motion.progress-duration with comp.file-item.motion.progress-easing. WHILE form is row the bar SHALL stand comp.file-item.progress.gap below the texts and as wide as them, with ends rounded by comp.file-item.progress.shape; WHILE form is tile it SHALL lie along the bottom edge of the box, inside it and as wide as it, clipped by the box's corners, over the picture or the ground. WHILE progress is null THE file item SHALL draw the bar as a linear progress indicator draws its indeterminate form (PRG-2), once per comp.file-item.motion.cycle-duration with comp.file-item.motion.cycle-easing, and SHALL NOT run it while the file item is not visible. WHERE reduced motion is requested THE file item SHALL show a new value at once and draw the indeterminate bar as PRG-7 says. In every other status THE file item SHALL draw no bar and, in a row, keep no room for one. THE file item SHALL never move the bar by itself: progress is the product's report of its own sending.
FIT-11 WHILE readOnly is false THE file item SHALL draw a remove action, and before it, comp.file-item.action.gap from it, a retry action WHILE status is error and retryable is true: each an icon-only button, a square of comp.file-item.action.size with corners of comp.file-item.action.shape, filled with the action container color of its form, holding the icon set's close glyph (remove) or refresh glyph (retry) at comp.file-item.action.icon.size in the action icon color of its form — at the end of a row (FIT-2), at the top end corner of a tile (FIT-3). WHEN the person activates one by pointer, touch, Enter or Space THE file item SHALL emit remove or retry exactly once with value and do nothing else: it SHALL NOT take itself away, change its status or move focus because of the activation. The actions SHALL NOT be inside the body (FIT-12) and SHALL NOT activate it. Each SHALL be a stop in the focus order after the body, retry before remove; THE file item SHALL handle no arrow key, Delete or Backspace, SHALL hold no selected state and draw no handle, and SHALL NOT be moved by dragging: an order that people change is the List's, in whose rows a file item is content.
FIT-12 WHILE interactive is true and href is empty THE file item SHALL make its body — the glyph or the picture, and the texts — exactly one focusable element with the button role, before the actions in the focus order, and WHEN the person activates it by pointer, touch, Enter or Space SHALL emit click exactly once with value. It SHALL open nothing itself: the product opens the file where it means to. WHILE interactive is false and href is empty THE file item's body SHALL NOT be focusable, SHALL NOT emit click, and SHALL draw no hover, focus or press answer. In a tile the body is the box and the texts beneath it; a press on an action's visible bounds SHALL go to that action and never to the body under it (FIT-18).
FIT-13 WHILE href is not empty THE file item SHALL make its body a link, whether interactive is true or false, as every control with a destination is: the platform's own link to href, with the link role and not the button role; activated by pointer, touch or Enter and not by Space; emitting click exactly once with value and then opening href where target names, unless the product cancelled that click, and never holding the navigation back for sys.motion.duration.press-hold; drawn from the same tokens in every state as a pressable body. WHILE target is new THE file item SHALL open href in a new context, SHALL expose targetLabel as the link's description after its name and role — in debug builds reporting an empty one — and WHILE form is row SHALL draw, comp.file-item.row.container.gap after the texts and before the actions, the glyph open_in_new of the system's icon set through the icon helper at comp.file-item.action.icon.size in comp.file-item.icon.color, hidden from assistive technology; a tile has no such place and draws none. WHILE disabled is true THE file item's link SHALL NOT be followed by any means the platform gives a link, SHALL keep the link role with the disabled state and no destination, and SHALL NOT be focusable. IF href is only white space, names a script to run, or cannot be read as an address THE file item SHALL be what interactive makes it, without a destination, and in the last two cases report it in debug builds. THE file item SHALL load nothing from href and SHALL NOT learn the file's name, size or format from it.
FIT-14 WHILE the body of a pressable file item is hovered, keyboard-focused or pressed THE file item SHALL render the body's container color from the component tokens of its form in the highest-precedence active state, with precedence pressed over focused over hovered, as a layer over the glyph or the picture and the texts — in a row over the body's bounds grown by comp.file-item.row.container.outset on every side, with corners of comp.file-item.row.container.shape, taking no room; in a tile over the box, with the box's corners. WHILE an action is hovered, keyboard-focused or pressed THE file item SHALL render that action's container, glyph and opacity from the action parts of the same tokens, with the same precedence. Focus gained by pointer or touch SHALL NOT select the focused state. The name, the second line, the glyph, the picture, the bar and the part that is not under the pointer, the press or the focus SHALL NOT change. WHEN the body or an action receives keyboard focus THE file item SHALL show the focus ring around it without animation — the system's focus indicator, sys.focus.offset from the part and following its corners, sys.focus.outer-width wide in the color role focus-ring, with the inner stroke of sys.focus.inner-width where the system has one; focus gained by pointer or touch SHALL NOT show it.
FIT-15 WHEN the body of a pressable file item or one of its actions is pressed by pointer, touch, Enter or Space THE file item SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. A body that is a link is not pressed by Space (FIT-13).
FIT-16 WHILE the body of a pressable file item or one of its actions is pressed THE file item SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole part and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; glyph, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.file-item.motion.feedback-duration with comp.file-item.motion.state-easing, fades over comp.file-item.motion.state-duration with comp.file-item.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
FIT-17 THE file item SHALL ease the corners of a tile's box, of a row's picture, of the body's state layer and of its actions by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show the keyboard focus of its body and of an action as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README.
FIT-18 WHERE the visual size of an action, or of the body of a pressable file item, is below the minimum target THE file item SHALL extend its hit area on both axes to sys.target.touch for touch input and sys.target.pointer for fine pointers, without changing its visual size; a platform that cannot tell the input type SHALL use sys.target.touch. WHILE the extended hit areas of two parts overlap — the retry and the remove action, an action and the body, or the actions of two file items — THE file item SHALL give a press on a part's visible bounds to that part, a press on an action's visible bounds to the action even where it lies over the body, and a press between two actions to the nearer one. THE file item SHALL set two actions that stand side by side — the retry and the remove action — apart by comp.file-item.action.gap, or by twice the amount by which the minimum target in force exceeds comp.file-item.action.size where that is more, so that the hit area of each reaches the minimum target on its own half of the space between them; it SHALL NOT shrink the hit area of either below the minimum target to keep them nearer, and the hit area of the action at the item's end SHALL still reach no further than the item's end edge.
FIT-19 THE file item SHALL expose itself to assistive technology as a group named by name, in every form and whatever nameHidden says, read in this order: name; formatText, sizeText, then statusText or — WHILE status is error — errorText; then, WHILE status is uploading, the bar as a progress bar named by name whose value is progress as a share of 0 to 1, or busy WHILE progress is null; then the retry action WHERE it is drawn, as a button named retryLabel, and the remove action WHERE it is drawn, as a button named removeLabel, each with its place {name} filled by name, so that "Remove brief.pdf" is the action's name and two files' actions are told apart wherever names are listed; neither action SHALL carry the name a second time as a description. WHILE the body is pressable it SHALL be a button, or a link (FIT-13), named by name and described by the second line's texts. WHILE a component holds the file item as one of the items of a list of its own THE file item SHALL be exposed as an item of that list in the group's place, by the word of that component and through no prop of its own. The glyph, the picture, the status mark and the mark of a new context SHALL be kept from assistive technology. THE file item SHALL have no live region and SHALL announce nothing — not that it appeared, not a change of status or of progress: what is said of many files is said once, by whoever holds them.
FIT-20 THE file item SHALL have no words of its own: it SHALL take removeLabel and retryLabel from the instance's prop and, WHILE that is empty, from the product's words of the keys removeLabel and retryLabel, and name, formatText, sizeText, statusText, errorText and targetLabel from the product alone, and from nowhere else. IF neither gives removeLabel or retryLabel THE file item SHALL still draw the action, expose it without a name, and in debug builds report the missing word once per page or screen, naming the prop. IF the word given has no place {name} THE file item SHALL name the action with it as it is written and in debug builds report that every file's action has the same name; another text between braces, and a brace that opens or closes nothing, SHALL be kept as written and reported. THE file item SHALL build no word from mediaType, from the end of name or from a number: it SHALL NOT write a format, a size or a share of progress in words.
FIT-21 WHILE disabled is true THE file item SHALL render each action from the disabled tokens of its form and disable it, so that it leaves the focus order — an action keeps the container it has at rest, none in a row and the disc in a tile, and only its glyph fades, as a quiet icon-only button grows no fill when it is disabled; SHALL make the body not pressable and not focusable, drawing no hover, focus or press answer on it; and SHALL emit neither click, remove nor retry. The name, the second line, the glyph, the picture, the status mark and the bar SHALL stay drawn as they are and readable by assistive technology: what was chosen is still what was chosen.
FIT-22 WHILE readOnly is true THE file item SHALL draw neither the remove action nor the retry action, SHALL keep no room for them — a row's texts reach its end edge — and SHALL emit neither remove nor retry. Everything else SHALL hold as without it: the status, the bar and the error are drawn, and the body is pressable WHERE interactive or href makes it so.
FIT-23 WHEN the colors of the body or of an action change between states THE file item SHALL animate the change over comp.file-item.motion.state-duration with comp.file-item.motion.state-easing, and SHALL change them at once where motion is reduced. A change of form, of status, of a text, of the glyph or of the picture SHALL be drawn at once, without a motion of the file item's own. THE file item SHALL NOT run a picture that moves: WHERE the platform can hold a moving picture still it SHALL draw its first frame, and where it cannot (the web's image element) it SHALL leave the picture to the platform and state that in its README.
FIT-24 WHILE the text size is enlarged THE file item SHALL grow with its texts: a row's lines wrap and are never cut, its glyph keeps its box, its picture comp.file-item.row.media.height and its actions comp.file-item.action.size; a tile's box keeps the size the product or the token gives, its name stays one line and grows in height, and its second line wraps beneath it. WHERE what stands beside a row's texts — its glyph or picture, the mark of a new context, its actions and the gaps between them — would leave the texts less than half the width of the row (a narrow screen, enlarged text) THE file item SHALL place the actions on a line of their own below the texts and the bar, at the row's end and in the same order, the line as tall as the minimum target in force, so that the name and the second line take the whole width after the glyph; otherwise a row's actions SHALL stay at its end beside its first lines. The answer SHALL be the same wherever the actions stand now, so that placing them does not change it. At 200 % in a width of 320 no text SHALL be lost or lie over another part, a tile SHALL be no wider than its layout (FIT-3), and nothing SHALL need scrolling in two directions.
FIT-25 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 item SHALL draw its texts and glyphs in the platform's text color, a link's name in the platform's link color; SHALL draw the bar with its indicator in the platform's highlight color on a track edged in the text color; SHALL draw a tile's box and a row's picture box with an edge in the text color, since their ground is gone; SHALL draw each action of a tile on the platform's surface color with an edge in the text color; SHALL draw disabled actions in the platform's disabled text color; SHALL leave a picture as it is; and SHALL rely on no color of its own to tell a failed file from another: its words and its mark's shape do.
FIT-26 WHERE the layout direction is right-to-left THE file item SHALL mirror its layout: a row's glyph or picture at the right, its texts after it toward the left, the mark of a new context and the actions at the left with remove outermost; a tile's actions at the top left corner of its box with remove outermost; the status mark at the right of its words; and the bar filling — and its indeterminate segment moving — from right to left. A picture SHALL never be mirrored, and a ripple SHALL still grow from the point of contact.
FIT-27 THE file item 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 item is shown THE file item SHALL draw itself for the new direction at once, without a motion of its own, keeping its focus.
FIT-28 THE file item SHALL lay out each text it draws — the name, the format, the size, the status and the error — as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a file name in Latin letters keeps its order, its dot and its extension in a right-to-left row, and the cut of FIT-9 keeps the name's own ending wherever that is drawn. WHERE the layout direction is right-to-left a glyph SHALL be mirrored exactly when the system's icon set marks its name as mirrored or the product forces it, by the icon helper and inside its own box; THE file item SHALL mirror no glyph itself.
FIT-29 WHEN form, status, fit or target names a value the file item does not know THE file item SHALL behave as row, pending, cover and same; IF width or height is given and is not a number above zero THE file item SHALL behave as if it were null; IF glyph names a glyph the icon set does not have THE file item SHALL draw nothing in its box and keep the box; and in each case debug builds SHALL report it. IF preview is true WHILE previewSrc is empty and the product gave no file THE file item SHALL draw the glyph and report nothing: a product may give the file later.
FIT-30 WHEN a prop changes while the file item is shown THE file item SHALL draw and expose the new value at once, keeping its focus, and SHALL announce nothing (FIT-19). WHEN the retry action leaves — status is no longer error, retryable or readOnly changed — while focus is on it THE file item SHALL move focus to its remove action, or to its body WHERE that is pressable and no remove action is drawn. THE file item SHALL NOT move focus otherwise, and where focus goes when a file item itself is taken away is its holder's to say.
FIT-31 WHILE the system's icon set cannot yet draw a glyph (its font is still loading) THE file item SHALL draw nothing in that glyph's place, never the glyph's name as text, SHALL keep the glyph's box, and SHALL draw the glyph once the set can; removeLabel and retryLabel SHALL still reach assistive technology.
FIT-32 IF the file item holds a child that is not the glyph of its icon slot THE file item SHALL NOT draw it and in debug builds report it: the file item has no place for content of the product's — a menu of further actions, a caption, a checkbox — and a product that needs one puts it beside the file item, in its own layout or in the trailing place of a List's row. A platform that cannot inspect its content SHALL state the rule in its README and docs page.
FIT-33 THE file item SHALL NOT upload and SHALL NOT know more of a file than its props say: it SHALL send no file or part of one to anyone, SHALL keep no file beyond the handle the product gave it for FIT-6, SHALL set neither status nor progress itself, SHALL take name and mediaType, WHERE the product left them empty and gave the file itself, from what the platform reports of that file without opening it, and nothing else from it, and SHALL load nothing about a destination. This holds on every platform.

各平台名称对照

对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。

规格 Web SwiftUI Jetpack Compose Flutter React Native
组件 tanrei-file-item TanreiFileItem TanreiFileItem TanreiFileItem FileItem
form · 属性 form form form form form
value · 属性 value id key key value
name · 属性 name name name name name
mediaType · 属性 mediaType mediaType mediaType mediaType mediaType
glyph · 属性 glyph glyph glyph glyph glyph
formatText · 属性 formatText formatText formatText formatText formatText
sizeText · 属性 sizeText sizeText sizeText sizeText sizeText
status · 属性 status status status status status
statusText · 属性 statusText statusText statusText statusText statusText
progress · 属性 progress progress progress progress progress
errorText · 属性 errorText errorText errorText errorText errorText
retryable · 属性 retryable isRetryable retryable retryable retryable
preview · 属性 preview showsPreview showPreview showPreview preview
previewSrc · 属性 previewSrc previewURL previewModel previewImage previewSource
fit · 属性 fit contentMode contentScale fit resizeMode
nameHidden · 属性 nameHidden isNameHidden nameHidden nameHidden nameHidden
width · 属性 width width width width width
height · 属性 height height height height height
interactive · 属性 interactive action onClick onPressed onPress
href · 属性 href destination href href href
target · 属性 target opens target target target
targetLabel · 属性 targetLabel targetLabel targetLabel targetLabel targetLabel
removeLabel · 属性 removeLabel removeLabel removeContentDescription removeSemanticLabel removeAccessibilityLabel
retryLabel · 属性 retryLabel retryLabel retryContentDescription retrySemanticLabel retryAccessibilityLabel
disabled · 属性 disabled disabled enabled enabled disabled
readOnly · 属性 readOnly isReadOnly readOnly readOnly readOnly
click · 事件 click action onClick onPressed onPress
remove · 事件 remove onRemove onRemove onRemove onRemove
retry · 事件 retry onRetry onRetry onRetry onRetry
备注 The shadow-root form: a part with states and actions of its own. Class TanreiFileItem; React FileItem. <tanrei-file-item name="brief.pdf" size-text="820 KB" remove-label="Remove"></tanrei-file-item>; a tile: <tanrei-file-item form="tile" preview width="160" height="90" name="floor-plan.png"></tanrei-file-item>. Attributes media-type, format-text, size-text, status-text, error-text, preview-src, name-hidden, target-label, remove-label, retry-label, readonly; progress absent for null; width and height are numbers of CSS pixels, as an image's, and are the box of the tile alone. **The file**: the property file (a File or null, never an attribute) is the platform's handle. With preview and an image kind and no preview-src, the element creates one object URL for it when it first comes near the viewport (an IntersectionObserver), gives it to its own <img part="preview" alt="" decoding="async"> and to nothing else, and revokes it in disconnectedCallback, when file is set again and when preview, preview-src or the kind stops asking for it (FIT-7); reconnected, it makes a new one when it is next in view. name and media-type left empty are read from file.name and file.type (FIT-33). file is also what a File upload submits for the item (FUP-31). **The body** is <div part="container">, or <button part="container"> with interactive, or <a part="container" href> with href (the shared link primitive; web-only rel and download are forwarded to the anchor, and download is how a link saves the file in place of opening it); the two actions are native buttons beside it in the shadow root, with aria-label — the product's sentence with the file's name filled in — never inside it. In a tile the actions are placed over the box and come after the body in the DOM, so the order of focus is the order read. click, remove and retry are CustomEvents that bubble and are composed, with detail.value; click is cancelable for a link. The host has role="group" and aria-label from name; a File upload that holds it sets role="listitem" on it through an internal channel that is not part of its public surface (FIT-19). The cut of a tile's name (FIT-9) is two spans — the start, which shrinks with an ellipsis, and the ending, which does not — inside one <bdi>. Parts: container, media, icon, preview, headline, supporting-text, status-icon, progress, action. The bar is <tanrei-progress-indicator> (the small linear one), drawn from this component's own bar tokens. No form participation: a file item submits nothing; what holds it does. TanreiFileItem(name: "brief.pdf", sizeText: "820 KB", onRemove: { }); a tile: TanreiFileItem(name:, form: .tile, width: 160, height: 90, showsPreview: true, file: url). interactive is whether action is given (a nil action is not pressable); destination makes it a Link. The file is a URL: the picture is QLThumbnailGenerator's thumbnail for the box's size at the display's scale, asked for when the view appears and cancelled when it disappears (FIT-7); previewURL goes through AsyncImage. contentMode is .fill for cover and .fit for contain. disabled follows .disabled(_:). The middle cut is .truncationMode(.middle), which keeps the ending. TanreiFileItem(name = "brief.pdf", sizeText = "820 KB", onRemove = { }). interactive is whether onClick is given; disabled is the inverse flag enabled. The file is a content Uri: the picture is ContentResolver.loadThumbnail(uri, size, signal) for the box's size, started in a LaunchedEffect keyed by the Uri and cancelled when the item leaves composition (FIT-7); previewModel is whatever the product's image loader takes, drawn through a painter slot so that the package depends on no loader. contentScale is Crop for cover and Fit for contain. The middle cut is TextOverflow.MiddleEllipsis. A link has no link role: Compose has none for a clickable element. TanreiFileItem(name: 'brief.pdf', sizeText: '820 KB', onRemove: () {}). interactive is whether onPressed is given; disabled is the inverse flag enabled. The framework has no thumbnail of a file by its handle: previewImage is an ImageProvider the product gives (a FileImage with ResizeImage for a local picture, a NetworkImage for one its server made), and the file item makes none itself — FIT-7's address is the product's on this platform, and the README says so. fit is BoxFit.cover or BoxFit.contain. The middle cut is drawn by measuring with a TextPainter. A link opens through the opener the app provides. <FileItem name="brief.pdf" sizeText="820 KB" onRemove={…} />. interactive is whether onPress is given. previewSource is an Image source ({ uri }): for a picked file the product passes the picker's uri, which the platform's image view draws scaled down without the file item reading it; no address is made (FIT-7). resizeMode is cover or contain. The middle cut is ellipsizeMode="middle". A link opens through Linking.

令牌

令牌 引用 解析值(浅色)
--tanrei-comp-file-item-action-gap sys.space.space200 16px
--tanrei-comp-file-item-action-icon-size sys.icon.small 20px
--tanrei-comp-file-item-action-shape sys.shape.corner.full 9999px
--tanrei-comp-file-item-action-size — 32px
--tanrei-comp-file-item-disabled-opacity — 1
--tanrei-comp-file-item-done-icon-color sys.color.success #077b32
--tanrei-comp-file-item-error-icon-color sys.color.error #cd262f
--tanrei-comp-file-item-error-supporting-text-color sys.color.error #cd262f
--tanrei-comp-file-item-focused-opacity — 1
--tanrei-comp-file-item-headline-color sys.color.on-surface #1b1b1b
--tanrei-comp-file-item-hovered-opacity — 1
--tanrei-comp-file-item-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-item-media-color sys.color.surface-container #f6f6f6
--tanrei-comp-file-item-motion-cycle-duration sys.motion.duration.extra-long4 1000ms
--tanrei-comp-file-item-motion-cycle-easing sys.motion.easing.standard cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-file-item-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-file-item-motion-progress-duration sys.motion.duration.medium2 300ms
--tanrei-comp-file-item-motion-progress-easing sys.motion.easing.standard cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-file-item-motion-state-duration sys.motion.role.state.large 250ms
--tanrei-comp-file-item-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-file-item-pressed-opacity — 1
--tanrei-comp-file-item-progress-gap sys.space.space75 6px
--tanrei-comp-file-item-progress-indicator-color sys.color.primary #1b1b1b
--tanrei-comp-file-item-progress-shape sys.shape.corner.full 9999px
--tanrei-comp-file-item-progress-thickness — 2px
--tanrei-comp-file-item-progress-track-color sys.color.surface-variant #eeeeee
--tanrei-comp-file-item-row-action-container-color — #000000 · 0%
--tanrei-comp-file-item-row-action-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-item-row-action-state-layer-color comp.file-item.row.action.icon.color #4b4b4b
--tanrei-comp-file-item-row-container-color — #000000 · 0%
--tanrei-comp-file-item-row-container-gap sys.space.space150 12px
--tanrei-comp-file-item-row-container-outset sys.space.space50 4px
--tanrei-comp-file-item-row-container-shape sys.shape.corner.small 6px
--tanrei-comp-file-item-row-disabled-action-container-color — #000000 · 0%
--tanrei-comp-file-item-row-disabled-action-icon-color — #1b1b1b · 38%
--tanrei-comp-file-item-row-disabled-action-state-layer-color — #1b1b1b · 38%
--tanrei-comp-file-item-row-disabled-container-color — #000000 · 0%
--tanrei-comp-file-item-row-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-file-item-row-focused-action-container-color — #4b4b4b · 10%
--tanrei-comp-file-item-row-focused-action-icon-color — #4b4b4b
--tanrei-comp-file-item-row-focused-action-state-layer-color — #4b4b4b
--tanrei-comp-file-item-row-focused-container-color — #1b1b1b · 10%
--tanrei-comp-file-item-row-focused-state-layer-color — #1b1b1b
--tanrei-comp-file-item-row-hovered-action-container-color — #4b4b4b · 8%
--tanrei-comp-file-item-row-hovered-action-icon-color — #4b4b4b
--tanrei-comp-file-item-row-hovered-action-state-layer-color — #4b4b4b
--tanrei-comp-file-item-row-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-file-item-row-hovered-state-layer-color — #1b1b1b
--tanrei-comp-file-item-row-icon-size sys.icon.medium 24px
--tanrei-comp-file-item-row-media-height — 40px
--tanrei-comp-file-item-row-media-shape sys.shape.corner.small 6px
--tanrei-comp-file-item-row-pressed-action-container-color — #4b4b4b · 10%
--tanrei-comp-file-item-row-pressed-action-icon-color — #4b4b4b
--tanrei-comp-file-item-row-pressed-action-state-layer-color — #4b4b4b
--tanrei-comp-file-item-row-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-file-item-row-pressed-state-layer-color — #1b1b1b
--tanrei-comp-file-item-row-state-layer-color comp.file-item.headline.color #1b1b1b
--tanrei-comp-file-item-status-icon-gap sys.space.space50 4px
--tanrei-comp-file-item-status-icon-size sys.icon.extra-small 16px
--tanrei-comp-file-item-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-item-tile-action-container-color sys.color.surface-container-lowest #ffffff
--tanrei-comp-file-item-tile-action-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-item-tile-action-inset sys.space.space50 4px
--tanrei-comp-file-item-tile-action-state-layer-color comp.file-item.tile.action.icon.color #4b4b4b
--tanrei-comp-file-item-tile-container-color — #000000 · 0%
--tanrei-comp-file-item-tile-container-gap sys.space.space75 6px
--tanrei-comp-file-item-tile-container-shape sys.shape.corner.medium 8px
--tanrei-comp-file-item-tile-disabled-action-container-color — #ffffff
--tanrei-comp-file-item-tile-disabled-action-icon-color — #1b1b1b · 38%
--tanrei-comp-file-item-tile-disabled-action-state-layer-color — #1b1b1b · 38%
--tanrei-comp-file-item-tile-disabled-container-color — #000000 · 0%
--tanrei-comp-file-item-tile-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-file-item-tile-focused-action-container-color — #ededed
--tanrei-comp-file-item-tile-focused-action-icon-color — #4b4b4b
--tanrei-comp-file-item-tile-focused-action-state-layer-color — #4b4b4b
--tanrei-comp-file-item-tile-focused-container-color — #1b1b1b · 10%
--tanrei-comp-file-item-tile-focused-state-layer-color — #1b1b1b
--tanrei-comp-file-item-tile-hovered-action-container-color — #f1f1f1
--tanrei-comp-file-item-tile-hovered-action-icon-color — #4b4b4b
--tanrei-comp-file-item-tile-hovered-action-state-layer-color — #4b4b4b
--tanrei-comp-file-item-tile-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-file-item-tile-hovered-state-layer-color — #1b1b1b
--tanrei-comp-file-item-tile-icon-size sys.icon.large 32px
--tanrei-comp-file-item-tile-media-height — 96px
--tanrei-comp-file-item-tile-pressed-action-container-color — #ededed
--tanrei-comp-file-item-tile-pressed-action-icon-color — #4b4b4b
--tanrei-comp-file-item-tile-pressed-action-state-layer-color — #4b4b4b
--tanrei-comp-file-item-tile-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-file-item-tile-pressed-state-layer-color — #1b1b1b
--tanrei-comp-file-item-tile-state-layer-color comp.file-item.headline.color #1b1b1b
--tanrei-comp-file-item-headline-typescale sys.typescale.body 400 14px/1.5714
--tanrei-comp-file-item-supporting-text-typescale sys.typescale.body-small 400 13px/1.5385

测试样例

作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。

生成自 specs/file-item.spec.json, tokens/comp/file-item.tokens.json, design/tokens/

指南

两种形式

同样的三个文件,分别显示为行和图块

方面 row(默认) tile
用在哪里 一列任意种类的文件。 一排图片。
宽度 由它所在的位置决定。 方框的宽度:width,绝不超过它所在位置的宽度。
字形或图片 在名称前面。设置 preview 时,是一个小正方形。 占满整个方框,宽 width、高 height。
名称 换行,绝不截断。 方框下方的一行,从中间截断,以便保留末尾。
第二行 在名称下面。 在方框下方,在方框的宽度内换行。
进度条 在各项文本下面。 沿着方框的底边。
操作 在末尾:先是重试,再是移除。 在方框内,位于顶部靠末尾侧的角上。

width 和 height 是数字,只在图块形式下读取。没有 height 时,方框的高度由令牌 comp.file-item.tile.media.height 决定;没有 width 时,方框是正方形。请按你手头图片的比例来选:屏幕截图用 width="160" height="90",人物肖像用正方形。方框的宽度绝不会小于它的两个操作所需的宽度。

没有第三种紧凑形式。只占一行的文件,就是第二行什么都没有的行。

显示哪些内容

每个部分在你给出它时才显示。preview 和 nameHidden 这两个只有开和关的属性会改变显示的内容。没有哪个属性用来列出要显示的部分。

部分 何时显示
字形 只要没有绘制图片,就始终显示。
图片 设置了 preview,并且有图片可以绘制。
名称 在行里始终显示。在图块里也显示,只有设置了 nameHidden 并且绘制了图片时除外。
用文字写出的格式 你给出了 formatText。Tanrei 没有表示任何格式的文字。
大小 你给出了 sizeText,也就是按读者的语言格式化好的大小。
用文字写出的状态 你给出了 statusText。
错误 status 是 error,并且你给出了 errorText。它在任何形式下都占据状态的位置,绝不截断。
进度条 status 是 uploading。填充到 progress;未设置 progress 时是不确定的形式。

已完成的文件和失败的文件,在状态文字前面各有一个标记:对勾和错误标记。名称前面的字形不随状态变化。这样用户只需记住两个位置:名称前面说明文件是什么;状态前面说明它现在怎么样。

nameHidden 用于排得很密的一排图片。只有在绘制了图片时,它才隐藏名称。显示字形的图块会保留名称,因为表示格式的字形分辨不了同一格式的两个文件。无论哪种情况,对辅助技术来说,这个名称始终是文件项的名称。

字形表示格式

字形共有十个种类。每个种类都有一份媒体类型清单和一份文件扩展名清单。字形根据 mediaType 选定。媒体类型为空、是 application/octet-stream,或者不在这些清单的任何一份里时,则根据 name 的扩展名选定。各个种类按下表的顺序匹配。这些清单是固定的,在每个平台上都相同,所以同一个文件无论显示在哪里,画出的都是同一个字形。video/mp2t 也不决定种类:平台根据扩展名 .ts 推断出它,而 TypeScript 文件和视频流共用这个扩展名,所以 index.ts 画的是代码字形。

十个种类

种类 字形 媒体类型 后缀
pdf picture_as_pdf application/pdf pdf
spreadsheet table text/csv, text/tab-separated-values, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.oasis.opendocument.spreadsheet, application/vnd.apple.numbers, application/x-iwork-numbers-sffnumbers xls, xlsx, ods, numbers, csv, tsv
presentation slideshow application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.oasis.opendocument.presentation, application/vnd.apple.keynote, application/x-iwork-keynote-sffkey ppt, pptx, odp, key
archive folder_zip application/zip, application/x-zip-compressed, application/x-tar, application/gzip, application/x-gzip, application/x-bzip2, application/x-xz, application/x-7z-compressed, application/vnd.rar, application/x-rar-compressed zip, tar, gz, tgz, bz2, xz, 7z, rar
code code text/html, text/css, text/javascript, application/javascript, application/x-javascript, text/typescript, application/typescript, text/jsx, text/tsx, application/json, application/xml, text/xml, text/yaml, text/x-yaml, application/yaml, application/x-yaml, application/x-sh, text/x-sh, text/x-shellscript, text/x-python, text/x-script.python, text/x-ruby, application/x-ruby, text/x-go, text/x-rust, text/x-java, text/x-java-source, text/x-kotlin, text/x-swift, text/x-c, text/x-chdr, text/x-csrc, text/x-c++, text/x-c++src, text/x-csharp, text/x-php, application/x-php, application/x-httpd-php, application/sql, text/x-sql html, css, js, mjs, ts, tsx, jsx, json, xml, yaml, yml, sh, py, rb, go, rs, java, kt, swift, c, h, cpp, cs, php, sql
image image 所有以 image/ 开头的类型 png, jpg, jpeg, gif, webp, avif, svg, bmp, tif, tiff, heic, heif, ico
video movie 所有以 video/ 开头的类型 mp4, mov, m4v, webm, mkv, avi
audio music_note 所有以 audio/ 开头的类型 mp3, wav, m4a, aac, ogg, oga, flac, opus
document description 其余所有以 text/ 开头的类型,以及 application/rtf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.oasis.opendocument.text, application/vnd.apple.pages, application/x-iwork-pages-sffpages txt, md, rtf, doc, docx, odt, pages
other draft — 其余的一切,以及不带点的名称
  • 媒体类型先于后缀起决定作用。只要文件的媒体类型在某个清单里,它就属于那个种类,不管它的名称以什么结尾。
  • 绝不会为了弄清格式而打开文件。
  • 对于你自己的格式,用 glyph 指定图标集里的一个字形,或者把你自己的字形放进 icon 插槽。
  • 所有字形都只有一种颜色。格式没有自己的颜色:一种颜色无论用在哪里都只表示一个意思,而格式不是状态。
  • 格式要紧的地方,用 formatText 把它写成文字。字形对辅助技术是隐藏的。

预览及其代价

preview 在字形的位置绘制文件的一张图片。图片有两个来源:

  • previewSrc:你的服务器为这个文件生成的小尺寸版本的地址。任何格式的文件都可以这样绘制。
  • 文件本身:previewSrc 为空,并且文件是图片时。在 Web 上,你把 File 赋给 file 属性。

fit 决定图片怎样填充它的方框。默认值 cover 填满方框,裁掉放不下的部分,适合照片。contain 在方框的底色上显示完整的图片,适合扫描件、签名和示意图这类边缘也要紧的图。

160 × 90 的方框:cover、contain,以及隐藏名称

预览的代价:

图片的来源 代价
不设置 preview(默认) 没有代价。一百个文件项的开销只是它们的文本的开销。
使用 previewSrc 每个文件项一次图片请求,由平台自己的图片加载机制发出。
使用文件本身 为文件生成一个地址。文件项在第一次进入视口或接近视口时生成它;在文件项离开页面、你给了它另一个文件或不再给文件,以及不再需要预览时释放它。平台的图片解码器读取文件来绘制它。文件项自己什么都不读。
  • 只有图片才会直接由文件本身绘制。视频的一帧或文档的一页都需要打开并读取文件,而 Tanrei 的任何组件都不做这件事。这类文件请给出 previewSrc。
  • 在图片可以绘制之前,以及图片无法绘制时,绘制的是表示格式的字形。没有加载指示器,也没有图片损坏的标记,status 也不会改动:缩略图没有加载出来,并不是文件的失败。
  • 图片没有替代文本。它占的是原来字形的位置,而文件由 name 命名。

可按下、链接或只读

默认情况下,文件项的主体不接收焦点,也不响应任何操作。文件项只是承载着它的操作。

主体是按钮、主体是在新标签页中打开的链接,以及只读的文件

属性 主体是 用在哪里
interactive 一个按钮。它触发带有 value 的 click。 在用户所在的地方打开文件:在你自己的查看器里打开图片,在面板里打开文档。
href 指向文件的链接。它先触发 click;取消这个事件,就可以由你自己来处理导航。 你的服务器已经持有的文件。设置 target="new" 时,要给出 targetLabel。
两者都没有 不可按下。 只是列出来的文件。

主体是字形或图片,再加上各项文本。操作在主体旁边,绝不在主体里面,所以可按下的文件项最多有三个焦点停靠点:主体、重试、移除。

readOnly 不绘制这两个操作,也不为它们留出位置。它用于只供阅读的记录里的文件。主体仍然可以按下。

disabled 禁用操作和主体。文件项显示的内容保持原样,并且仍然可读。禁用的操作保留静止时的底色:行里没有底色,图块里是圆形底;只有字形变淡。

顺序可由用户调整的文件

文件项没有手柄,也没有自己的顺序。调整顺序是列表的事:把每个文件项放进可调整顺序的列表的一项中。列表在文件项旁绘制手柄,让整个项可以拖动,并从手柄打开移动菜单;文件项保留自己的字形、文本、进度条和操作。

顺序可由用户调整的文件:可调整顺序的列表中的文件项

<tanrei-list reorderable gap="small" label="Attachments" drag-handle-label="Reorder {label}"
             move-to-start-label="Move to top" move-before-label="Move up"
             move-after-label="Move down" move-to-end-label="Move to bottom"
             lifted-text="{label} lifted: {position} of {count}"
             moved-text="{label}: {position} of {count}"
             cancelled-text="{label} returned to {position} of {count}">
  <tanrei-list-item><tanrei-file-item name="cover-letter.pdf" size-text="120 KB" remove-label="Remove {name}"></tanrei-file-item></tanrei-list-item>
  …
</tanrei-list>

列表本身不移动任何东西:收到 move 时,请把项排成新的顺序。列表会播报每次移动,并让焦点保持在被移动项的手柄上;详见列表。每个列表项以其文件命名(文件名就是内容的文本,无需其他设置)。文件上传也用同样的方式排列文件。

文字

文件项没有自己的文字。其中两项可以用 setWords() 为整个产品只给一次;其余的文本都是实例的属性。

属性 产品文字的键 没有它时
removeLabel removeLabel 移除操作照样绘制,但没有名称。调试构建会报告这一点。
retryLabel retryLabel 重试操作照样绘制,但没有名称。调试构建会报告这一点。

每个操作的名称里都有它的文件名:removeLabel 和 retryLabel 是带占位符 {name} 的句子,由文件项填入文件名(“移除 {name}”)。一个句子对每个文件都够用,用户按你的语言的语序听到“移除 brief.pdf,按钮”,屏幕阅读器列出页面上的按钮时也是如此。没有 {name} 的句子会让每个文件的操作同名,调试构建会报告这一点。

从右到左

在从右到左的布局中,字形或图片位于右侧,操作位于左侧,其中移除在最外侧;进度条从右侧开始填充,图块的操作位于它的左上角。图片绝不镜像。用拉丁字母写的文件名保持自己的顺序,连同它的点和扩展名。十个格式字形中,只有 other 的字形会镜像,因为图标集是这样标记它的。

推荐

  • 给已完成的文件写 statusText,给失败的文件写 errorText。状态绝不能只靠标记或颜色来表达。
  • 只有重试可能解决的失败才设置 retryable。过大的文件再试一次还是过大。
  • 大小由你自己格式化。1 MB 是 1000 KB 还是 1024 KB,由你的产品决定。
  • 把属于同一组的文件项放进你自己的列表(一个带名称的 <ul>),或者放进文件上传。
  • 移除一个文件项之后,由你自己移动焦点。文件项单独存在时,无从知道它旁边是什么。

避免

  • 不要指望文件项自己移除自己、改变自己的状态或移动自己的进度条。它只绘制你设置的内容。
  • 不要对不是图片的文件使用 nameHidden。它们的图块无论如何都会保留名称。
  • 不要在它里面放菜单、复选框或说明文字。它唯一的插槽是字形。对文件的其他操作放在文件项旁边。
  • 不要把它用作供用户选择或调整顺序的列表中的行。那是列表的行。

无障碍

角色
group
标注
A group named by the file's name — also in a tile that does not draw it — reading the name, the format, the size and the status or the 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 its place ({name}). A pressable body is a button, or a link, named by the file's name and described by the second line. Inside a File upload the file item is an item of the upload's list in the group's place (FUP-25).
最小目标
即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px

键盘操作

按键 动作
Tab / Shift+Tab Moves focus to the body, where the file item is pressable, then to its retry action where it has one, then to its remove action. A file item that is not pressable and is read-only is no stop at all.
Enter / Space On a pressable body: emits click, for the product to open the file. On an action: asks whoever holds the file item to retry or to remove the file.
Enter On a body that is a link: follows it. Space does what it does on any link (FIT-13).

备注

  • The name is the name in every form (FIT-8, FIT-19): hiding it in a strip of pictures hides it from sight alone, and only behind a picture that is drawn. A picture is no text alternative for a name, and the file item asks for no description of the picture: the picture stands for the file, which has a name. A product that shows the picture as content — to be looked at — shows it in an Image preview, whose images have their own alternative text.
  • Status is never color alone (WCAG 1.4.1): a done or failed file has words, and its mark has a shape of its own. The glyph before the name says the format and never the status, so that a person learns one place for each.
  • A failure is never hidden (WCAG 3.3.1): errorText is drawn in every form, wraps and is never cut, also beneath a tile whose name is hidden.
  • A format has no color. Ten colors for ten kinds would be ten meanings a person must learn, would compete with the two status colors on the same line, and would say nothing to a person who does not tell them apart; the glyph's shape and, where the product has them, the format's words carry it.
  • The remove and retry actions are named by one sentence of the product's with the file's name in it, so a person tabbing through a list, or listing the buttons of the page, hears "Remove brief.pdf, button" — in the order of the product's language — without the product writing a sentence per file.
  • Nothing is announced by a file item (FIT-19). A component that holds file items — a File upload, a chat composer — has one live region for all of them and says what was added, finished and failed there; a product that arranges file items itself does the same with a region of its own.
  • A cut name (FIT-9) is cut for sight alone and keeps its ending. The whole name is the accessible name; on the web it is also the body's or the name's `title` only where the product asks for one, because a tooltip is not reachable by touch and is no substitute for the row form, which never cuts.
  • A picture that moves (an animated image) is the platform's to hold still or not (FIT-23). On the web an image element plays it; a product whose strip must not move gives a still `previewSrc`.
  • Without script, and until the component is defined, on the web: nothing is drawn, because the name is an attribute. A page that must list files where script does not run writes its own links to them, as it would without Tanrei.
  • Compose and React Native: the group is a node with the name as its content description (Compose) or a labelled container (React Native), and the actions keep their own nodes.

生成自 specs/file-item.spec.json, tokens/sys/measure.tokens.json

组件负责的部分

  • 单独使用时,文件项是一个组,以文件名命名。在文件上传里,它是文件上传的列表中的一项。
  • 可按下的主体是一个按钮或一个链接,以文件名命名,以第二行作为描述。在焦点顺序中,它排在操作前面。链接用 Enter 激活,不用空格键;在新标签页中打开的链接,会在名称后面说出 targetLabel。
  • 操作是按钮,以 removeLabel 和 retryLabel 命名,文件名填在占位符 {name} 中。重试排在移除前面。
  • 进度条具有进度条角色,并以文件命名。错误是第二行的文本,在状态的位置读出。
  • 字形、图片和状态标记对辅助技术隐藏。它们表示的内容由文字说出。
  • 文件项什么都不朗读。持有许多文件项的一方,为它们全体提供一个实时区域,文件上传就是这样做的。
  • 完整的名称始终会传给辅助技术,即使图块把它截断了,或者 nameHidden 把它隐藏了。
  • 文本放大到 200% 时,行会换行,不丢失任何内容。如果字形和操作会让名称所剩的宽度不到行宽的一半,操作就移到文本下方单独的一行,与行尾对齐,名称占满整个宽度。图块的方框保持原来的大小,它的文本在方框下方随之变大。
  • 在强制颜色模式下,方框和图块上操作的圆形底板保留一道边线,进度条仍然可见。图片保持原样。
  • 状态之间的颜色变化会用很短的时间过渡;在减弱动态效果的环境中则立即完成。

Web 上的已知差距:

  • 会动的图片(例如 GIF 动图)由浏览器的图片元素播放。文件项无法让它静止。如果这一点要紧,请把这张图片的静态版本作为 previewSrc 给出。
  • 在元素完成定义之前,什么都不会绘制,因为名称是一个属性,而不是文档里的文本。必须在不运行脚本的地方列出文件的页面,要自己写链接。

需要你做的事

  • 始终给出 name。
  • 给出 removeLabel 和 retryLabel:写在元素上,或者用 setWords() 只给一次。
  • 每一种状态、每一次失败,都要用文字说明:statusText 和 errorText。
  • 设置 target="new" 时,要给出 targetLabel。
  • 给一组文件项配上你自己的列表和名称,并在你自己的一个实时区域里朗读添加了什么、完成了什么、什么失败了。文件上传会替你做这两件事。
  • 移除一个文件项之后,把焦点移到下一个文件的移除操作上,或者移到添加文件的那个控件上。