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

文件选择器

让用户通过平台自己的选择器来选择文件:可以从一个按钮打开,在位置足够的地方还可以把文件拖到一块区域上。它在选择之前用文字说明限制,对选中的内容作出判定,再把它接受的文件和它拒绝的文件交给你的产品。它什么都不保留,什么都不列出,也不参与表单。

content_copy以 Markdown 格式查看

进阶

大多数产品直接使用包含此组件的Chat composer, File upload。

类别
选择
令牌
comp.file-picker
层级
基础

概览

Web
tanrei-file-picker
schedule
SwiftUI
TanreiFilePicker
radio_button_unchecked
Jetpack Compose
TanreiFilePicker
radio_button_unchecked
Flutter
TanreiFilePicker
radio_button_unchecked
React Native
FilePicker
radio_button_unchecked

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

两种布局的文件选择器。在这里选择文件不会带来任何变化:页面不保留任何文件。

选择文件选择照片image

文件选择器是选择文件的途径,仅此而已。你自己的按钮打开平台的文件选择器。按钮周围可以有一块拖放区域,它也接收拖到上面的文件。限制用你的文字写在旁边。选中的内容会对照限制作出判定,再交给你:被接受的文件随 select 交出,被拒绝的文件随 reject 交出。

它是一个高级组件:一个不带任何附加功能的控件,供自己保留文件的产品使用。它不保留任何文件,不绘制列表,也不随表单提交任何东西。大多数产品需要的是文件上传,也就是这个选择器加上它下方的一列文件项,可以直接用在表单里。

使用它的组件是文件上传。如果文件的去处不是文件上传的列表,就不用文件上传,单独使用它:

  • 一个文件当场读取,不予保留:导入设置文件、导入有若干行数据的电子表格、选一张要裁剪的图片。
  • 文件显示在别处:作为正在撰写的消息里的附件、显示在画布上、显示在你自己的表格里。
  • 工具栏里的一个添加附件按钮,没有区域,也没有文本。

要接收放到整个界面任何位置的文件,就用放置区把这个界面包起来,并在这个界面上保留一个文件选择器,作为不用拖动也能选择文件的途径。

它什么都不保留

select 之后,文件选择器就已经忘了这些文件。第二次选择会像第一次那样判定。如果你的产品不保留 select 交给它的东西,就没有谁会保留。

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

用法

在 trigger 插槽里放一个按钮,并监听 select。

<tanrei-field label="图层文件" supporting-text="这个图层会替换你正在编辑的图层。">
  <tanrei-file-picker id="layer" accept=".json" constraints-text="一个 JSON 文件"
                      selection-text="未选择文件">
    <tanrei-button slot="trigger" variant="outlined">选择文件</tanrei-button>
  </tanrei-file-picker>
</tanrei-field>

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

  const picker = document.querySelector('#layer');
  picker.addEventListener('select', async ({ detail }) => {
    const [file] = detail.files;
    // 选择器什么都不保留。选了什么由你自己来说明,文件也由你按自己的需要处理。
    picker.selectionText = file.name;
    importLayer(JSON.parse(await file.text()));
  });
  // 被拒绝的文件带有一个原因,它是一个名称,而不是一句话。请用你自己的话来说。
  picker.addEventListener('reject', ({ detail }) => {
    const field = picker.closest('tanrei-field');
    field.errorText = detail.files[0].reason === 'type' ? '请选择 JSON 文件。' : '请只选择一个文件。';
    field.error = true;
  });
</script>
// 进阶组件(用于构成其他组件的部件)从 advanced 入口导入。
import { Button, Field } from '@tanrei/react';
import { FilePicker } from '@tanrei/react/advanced';

<Field label="图层文件" error={!!problem} errorText={problem}>
  <FilePicker accept={['.json']} constraintsText="一个 JSON 文件"
    selectionText={chosen?.name ?? '未选择文件'}
    onSelect={(event) => importLayer(event.detail.files[0])}
    onReject={(event) => explain(event.detail.files)}>
    <Button slot="trigger" variant="outlined">选择文件</Button>
  </FilePicker>
</Field>

通过别的途径到达你的产品的文件,例如粘贴进来的,或者放到页面其他部分上的,可以用 judge() 按同一条规则判定。它只给出答案,不触发任何事件:

const { accepted, refused } = picker.judge(event.clipboardData.files);

在 React 中,judge() 在 ref 上:ref.current.judge(files)。

规格

只有触发器;带有已选内容和限制;处于错误状态的拖放区域;已禁用

添加附件选择文件选择文件选择文件

构成

部件 说明
1 group The whole component: the trigger with its texts, or the drop area. Exposed as a named group while it has a name; it draws nothing of its own.
2 trigger The product's own Button in the trigger slot. Activating it opens the platform's own file picker. It is the one way to choose by keyboard, by touch and with assistive technology, in both layouts.
3 container · 可选 The drop area, in the drop-area layout: a bounded place around the glyph, the prompt, the trigger and the line of limits, which takes files dragged onto it and opens the picker when pressed. It carries the state answer.
4 outline · 可选 The drop area's edge, dashed or solid as the system says.
5 icon · 可选 The glyph at the top of the drop area: the icon set's upload glyph, or the one in the icon slot. Decorative.
6 label · 可选 The drop area's prompt (dropText): the words that say files can be dragged here.
7 supporting-text · 可选 The line that states the limits in words (constraintsText): which kinds of file, how large, how many. In the area, or below the trigger.
8 selection-text · 可选 In the button layout, the text beside the trigger that says what is chosen now (selectionText). Whoever holds the files writes it.

属性

属性 类型 默认值 说明
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.
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.
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.
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.
maxFiles number null The most files one choice or one drop may bring, while multiple is true. Files beyond it are refused (reject, reason count). Null sets no limit; ignored while multiple is false. The file picker counts nothing across choices: a product that keeps a list sets this to the room that is left — zero once the list is full, which refuses every file — or judges the room itself.
label string "" The accessible name of the file picker as a group ("Photo"). It is not drawn. Leave it empty inside a Field, whose label names the group and is drawn, and inside a component that names the whole; left empty elsewhere, no group is exposed and the trigger stands as the button it is.
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.
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.
selectionText string "" What stands beside the trigger in the button layout: what is chosen now, in the product's words or the file's own name ("No file chosen", "brief.pdf", "3 files"). Read with the trigger, as part of its description. The file picker keeps nothing and so never writes or changes it: whoever holds what was chosen says it, and leaves it empty where a list below already does. Ignored in the drop-area layout.
disabled boolean false Nothing can be chosen or dropped: the trigger is disabled and the area answers nothing. Inside a Field, disable the Field.
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.
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.

插槽

插槽 说明
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.
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.

事件

事件 说明
select Files were chosen in the picker or dropped on the area, and passed accept, maxSize and the count of one choice. Emitted once per choice, with those files as the platform's own handles, in the order the platform gave them. The file picker keeps none of them and reads none: the product, or the component that holds the file picker, does with them what it means to.
reject Files of one choice or one drop were refused. 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 in one choice). The reasons are names for the product's code, not words: the product says what happened, in its own words and in a place of its own — a Field's errorText, a row of its list, a message it has announced. The file picker draws and says nothing for a refusal.

状态

行为

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

ID 需求
FPK-1 THE file picker SHALL lay out one part, from its start edge: the trigger with its texts WHILE layout is button (FPK-2), the drop area WHILE layout is drop-area (FPK-3). 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, SHALL draw no list and no row for what was chosen, and SHALL draw no label, no help, no error text and no requirement mark of its own: those are the Field's (FPK-21).
FPK-2 WHILE layout is button THE file picker SHALL draw the trigger at its start edge at the size the product gave it and, WHILE selectionText is not empty, selectionText after it on the trigger's line, comp.file-picker.trigger.gap from it and centred on it, in comp.file-picker.selection-text.typescale and comp.file-picker.standard.selection-text.color, wrapping below the trigger where the line is too short; and WHILE constraintsText is not empty SHALL draw it comp.file-picker.supporting-text.gap below that line, from the start edge, in comp.file-picker.supporting-text.typescale and comp.file-picker.standard.supporting-text.color, on as many lines as it needs and never cut. WHILE layout is button THE file picker SHALL draw no area, edge or glyph, SHALL ignore dropText and the icon slot, and SHALL take no drop (FPK-13). THE file picker SHALL NOT write selectionText itself and SHALL NOT change it because of a choice: it keeps nothing (FPK-7), so what is chosen now is said by whoever holds it.
FPK-3 WHILE layout is drop-area THE file picker SHALL draw the drop area across its whole width: a container with corners of comp.file-picker.container.shape, filled with comp.file-picker.standard.container.color, with an edge comp.file-picker.outline.width wide in comp.file-picker.standard.outline.color drawn inside its size, and inside it — comp.file-picker.container.padding-block from its top and bottom and comp.file-picker.container.padding-inline from its sides — one column, each part centred on the inline axis and comp.file-picker.container.gap from the next: the glyph (FPK-4), dropText WHILE it is not empty in comp.file-picker.label.typescale and comp.file-picker.standard.label.color, the trigger at the size the product gave it, and constraintsText WHILE it is not empty in comp.file-picker.supporting-text.typescale and comp.file-picker.standard.supporting-text.color. Each text SHALL be centred, wrap onto as many lines as it needs and never be cut. The area SHALL be as tall as its parts and paddings and have no height of its own. WHILE layout is drop-area THE file picker SHALL NOT draw selectionText: what was dropped on a place is shown below it, by whoever holds it.
FPK-4 THE file picker SHALL draw the drop area's edge as comp.file-picker.outline.style names: dashed, as dashes comp.file-picker.outline.dash-length long with gaps of the same length, following the corners; solid, as one unbroken line. It SHALL draw at the top of the area the icon set's upload glyph, or the glyph in the icon slot WHILE that slot holds one, at comp.file-picker.icon.size in comp.file-picker.standard.icon.color, with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, and SHALL keep it from assistive technology. A platform that cannot draw a dashed edge along eased corners SHALL draw it solid and state that in its README.
FPK-5 THE file picker SHALL take as its trigger the one element in the trigger slot — a Button of the product's, with the product's label, variant and size — and SHALL leave it what it is: drawn from the Button's own tokens, answering hover, focus and press itself, named by its own label. WHEN the trigger is activated, by any input the Button takes, THE file picker SHALL open the platform's own file picker (FPK-6). IF the trigger slot is empty THE file picker SHALL still render and still take drops and presses on its area, and in debug builds SHALL report that files cannot be chosen by keyboard, by touch or with assistive technology, which is a functional failure; IF it holds more than one element THE file picker SHALL use the first and report the others.
FPK-6 WHEN the picker is asked for THE file picker SHALL open the platform's own file picker — never a list of files it draws itself — asking it for one file WHILE multiple is false and for any number WHILE multiple is true, and to offer the kinds accept lists WHILE accept is not empty. WHEN the person chooses, THE file picker SHALL judge what was chosen (FPK-9) and leave focus on the trigger; WHEN the person leaves the picker without choosing THE file picker SHALL emit nothing, change nothing and leave focus on the trigger. WHILE a picker it opened is still open THE file picker SHALL NOT open a second.
FPK-7 THE file picker SHALL keep nothing and send nothing: 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 — SHALL NOT copy or store a file, and SHALL NOT hold a file, or any record of an earlier choice, beyond the turn in which it hands the files of one choice over (FPK-9): a second choice SHALL be judged as if it were the first. What is known of a file SHALL be what the platform reports of it without opening it: its name, its size and its kind. THE file picker SHALL take no part in a form: it has no name and no value, submits nothing, reports no missing value, and has nothing to reset or to restore. The files of a form are submitted by the component that keeps them, or sent by the product.
FPK-8 WHEN the drop area of the file picker is pressed by pointer or touch THE file picker 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. The area answers no key (FPK-12): Enter and Space press the trigger, which answers as a Button does (BTN-8).
FPK-9 WHEN files arrive — chosen in the picker or dropped on the area — THE file picker SHALL judge them in the order the platform gave them, by what the platform reports without opening them, and keep none: WHILE multiple is false and more than one arrived, it SHALL refuse them all with the reason count, choosing none for the person; otherwise it SHALL refuse with the reason type a file whose kind matches no entry of a non-empty accept — an extension compared with the end of its name without regard to case, a media type with the kind the platform reports, a family such as image/* with every kind of that family — and anything that is a folder; SHALL refuse with the reason size a file larger than maxSize WHILE maxSize is a number; and, WHILE multiple is true and maxFiles is a number, SHALL refuse with the reason count each remaining file beyond the first maxFiles of them. It SHALL then emit select once with the files that passed, WHERE there are any, and reject once with the refused files and their reasons, WHERE there are any, in that order. THE file picker SHALL judge each arrival by itself: it knows of no earlier choice, so the room that is left in a list is judged by whoever keeps the list. THE file picker SHALL draw no row and no error and say no word because of a choice.
FPK-10 WHILE the drop area is pressed THE file picker 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; dim applies the pressed opacity to the whole area 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; outline, glyph, texts, 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-picker.motion.feedback-duration with comp.file-picker.motion.state-easing, fades over comp.file-picker.motion.state-duration with comp.file-picker.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
FPK-11 THE file picker SHALL ease the corners of its drop area 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); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. THE file picker SHALL draw no focus indicator of its own: keyboard focus is on the trigger, which shows it as sys.behavior.focus-style names, as every Button does (BTN-10).
FPK-12 WHEN the person presses the drop area outside the trigger, by pointer or touch, WHILE disabled is false THE file picker SHALL do what activating the trigger does (FPK-6) and move focus to the trigger, so that the whole area is one large target for the one action. The area SHALL NOT be a stop in the focus order, SHALL have no role and no name of its own, and SHALL answer no key: Enter and Space are the trigger's. A press that begins on the trigger SHALL be the trigger's alone and open the picker once.
FPK-13 WHILE something is dragged over the drop area and disabled is false THE file picker SHALL draw the area as receiving — filled with comp.file-picker.receiving.container.color, its edge in comp.file-picker.receiving.outline.color, its glyph, its prompt and its line of limits in comp.file-picker.receiving.content.color — and tell the platform that letting go here copies; or, WHERE the platform tells before the drop that nothing dragged can be taken — it holds no file, every file is of a kind accept refuses, or it holds more than one file while multiple is false — as refusing: the fill and the texts as at rest, the edge in comp.file-picker.refusing.outline.color, and the icon set's block glyph in the glyph's place in comp.file-picker.refusing.icon.color, telling the platform that letting go here does nothing. The condition SHALL begin when the drag enters the area's bounds and end when it leaves them or ends, not when it crosses a part inside the area; the trigger and the texts SHALL NOT answer the drag themselves; and a drag elsewhere on the page or the screen SHALL change nothing: the file picker watches no more than its own area. WHILE layout is button THE file picker SHALL take no drop and SHALL leave a drag over it to the platform and the product.
FPK-14 WHEN files are let go over the drop area WHILE disabled is false THE file picker SHALL end the condition of FPK-13 at once, take the drop so that the platform does not open or navigate to the file, and judge what was dropped exactly as a choice in the picker (FPK-9): a drop over a refusing area SHALL emit reject with what it holds and no select, and a drop that holds no file SHALL emit nothing. It SHALL NOT move focus because of a drop. WHILE disabled is true THE file picker SHALL draw neither condition, tell the platform that letting go here does nothing, and still take a drop over its area so that a file let go by mistake does not replace the page.
FPK-15 WHILE the pointer is over the drop area, or the area is pressed (FPK-12), THE file picker SHALL render the area's container, edge, glyph, prompt, line of limits and opacity from the component tokens of the highest-precedence active state, with precedence pressed over hovered, and the pointer over the trigger counts as over the area. THE drop area SHALL have no focused state: keyboard focus is on the trigger, which shows its own indicator. Among conditions, disabled SHALL win over refusing and receiving, which win over hovered and pressed; the error condition (FPK-16) SHALL change the edge alone and combine with every state.
FPK-16 WHILE error is true THE file picker SHALL draw the drop area's edge in comp.file-picker.standard.error.outline.color, in every state, and every other part as without the error; SHALL expose the trigger as invalid; and WHILE layout is button SHALL draw nothing for it, because the error is said by the words of the Field around it. THE file picker SHALL never set error itself: not for a refused file and not for a picker that was left without a choice. WHILE something is dragged over the area the conditions of FPK-13 SHALL be drawn in the error edge's place.
FPK-17 WHILE label is not empty, or a Field names it (FPK-21), THE file picker SHALL expose itself to assistive technology as a group of that name holding the trigger; WHILE neither gives a name THE file picker SHALL expose no group and no role of its own, and SHALL report nothing: its trigger is a button with a name of its own, and a component that holds the file picker names the whole itself. It SHALL give the trigger, after any description the trigger has of its own, a description made of constraintsText, then selectionText WHILE it is drawn (FPK-2), then what a Field or the component that holds it gives (FPK-21), and SHALL expose the trigger as required WHILE required is true and as invalid WHILE error is true. It SHALL NOT expose the drop area, its glyph or dropText as controls: dropText is read as the text it is, before the trigger. THE file picker SHALL have no live region and SHALL announce nothing: a choice is said by whoever keeps it.
FPK-18 THE file picker SHALL have no words of its own: it SHALL take every word it draws or says — the trigger's label, label, constraintsText, dropText and selectionText — from the product alone, by the trigger and the props of the instance, and SHALL read none of them from the product's words by a key, because none is said the same way wherever a file picker stands. IF dropText or selectionText is not given THE file picker SHALL draw nothing in its place. IF accept is not empty, or maxSize or maxFiles is a number, WHILE constraintsText is empty THE file picker SHALL still render and in debug builds report that a limit is enforced and not said. THE file picker SHALL build no sentence from accept, maxSize, maxFiles, a count of files or a size in bytes.
FPK-19 WHILE disabled is true THE file picker SHALL disable its trigger as that Button's own disabled does and give it back as it was WHEN disabled becomes false, never enabling a trigger the product disabled itself; SHALL render the drop area's container, edge, glyph, prompt and line of limits, and selectionText, from their disabled tokens; SHALL NOT answer a press on the area (FPK-12) or a drag over it (FPK-14); and SHALL emit neither select nor reject. The way of FPK-31 SHALL still answer: disabled stops the person, not the product.
FPK-20 WHEN the colors of the drop area change between states or conditions THE file picker SHALL animate the change over comp.file-picker.motion.state-duration with comp.file-picker.motion.state-easing, and SHALL change them at once where motion is reduced. WHEN selectionText appears, changes or leaves THE file picker SHALL draw that at once, without a motion of its own.
FPK-21 WHILE the file picker is the control of a Field THE file picker SHALL take from it what a Field gives its control (FLD-8, FLD-10, FLD-11, FLD-14): the Field's label as its 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 as the last part of the trigger's description; the invalid state as its own error condition (FPK-16); the required state as required; and the disabled state as disabled. WHEN the label of the Field is pressed THE file picker SHALL move focus to its trigger and open nothing. WHILE another component holds the file picker as its member THE file picker SHALL take the same from that component by the same exchange — a name or none, a description, and the three states, each as a value — so that it is tied inside a component exactly as it is tied by hand.
FPK-22 WHILE the text size is enlarged THE file picker SHALL grow with its texts: the prompt, the line of limits and selectionText wrap and are never cut; the glyph keeps its box; the area grows in height; and the trigger grows as the Button says. 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.
FPK-23 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 picker SHALL draw the drop area's edge and its texts in the platform's text color and no fill; SHALL draw the receiving area with an edge twice comp.file-picker.outline.width wide, solid, in the platform's highlight color, and the refusing area with its block glyph, so that neither rests on a fill the mode removes; and SHALL draw disabled parts in the platform's disabled text color.
FPK-24 WHERE the layout direction is right-to-left THE file picker SHALL mirror its layout: the trigger at the right, selectionText after it toward the left, and the line of limits from the right edge. The drop area's column stays centred. A ripple SHALL still grow from the point of contact.
FPK-25 THE file picker 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 picker is shown THE file picker SHALL draw itself for the new direction at once, without a motion of its own, keeping its focus.
FPK-26 THE file picker SHALL lay out each text it draws — the prompt, the line of limits and selectionText — 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's name given as selectionText keeps its order, its dot and its extension in a right-to-left layout. 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 picker SHALL mirror no glyph itself.
FPK-27 WHEN layout names a value the file picker does not know THE file picker SHALL behave as button; IF maxSize is not a number above zero THE file picker SHALL set no limit of size; IF maxFiles is not a whole number of zero or more THE file picker SHALL set no limit of count; IF an entry of accept is neither an extension with its dot nor a media type THE file picker SHALL ignore that entry; and in each case debug builds SHALL report it. WHILE maxFiles is zero and multiple is true THE file picker SHALL refuse every file that arrives with the reason count and report nothing: zero is the room a full list has left, and to read it as no limit would open the list when it is full.
FPK-28 WHEN a prop of the file picker changes while it is shown THE file picker SHALL draw and expose the new value at once, keeping its focus, and SHALL announce nothing. WHEN accept, maxSize, maxFiles or multiple changes THE file picker SHALL judge by the new value from the next arrival on. WHEN layout changes while something is dragged over the area THE file picker SHALL end the condition.
FPK-29 WHILE the system's icon set cannot yet draw a glyph of the file picker (its font is still loading) THE file picker 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.
FPK-30 IF the trigger slot holds an element that is not a Button THE file picker SHALL still open the picker when that element is activated by a press or a click, and in debug builds report it; IF the file picker holds a child that is neither its trigger nor its glyph THE file picker SHALL NOT draw it and in debug builds report it. A platform that cannot inspect its content SHALL state the rule in its README and docs page.
FPK-31 THE file picker SHALL offer the product, and a component that holds it, a way in the platform's idiom to have files judged that came neither through its picker nor onto its area — files pasted, dropped on another part of the product's screen, or made by the product: it SHALL judge them as one arrival by the rule of FPK-9 and answer with the files that passed and with the refused files and their reasons, each in the order given; SHALL emit neither select nor reject for them, as a control's events tell what a person did; and SHALL keep none of them (FPK-7).
FPK-32 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 picker 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 (FPK-17), which it SHALL be WHILE it has a name: its label or its Field's, or one given so — an id reference by the elements it names in the host's tree or a tree around it, a description after its own. WHILE label, or the Field's label, is not empty it is the group's name, over a name given so. 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 picker SHALL take it off that element.

各平台名称对照

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

规格 Web SwiftUI Jetpack Compose Flutter React Native
组件 tanrei-file-picker TanreiFilePicker TanreiFilePicker TanreiFilePicker FilePicker
layout · 属性 layout layout layout layout layout
multiple · 属性 multiple allowsMultipleSelection multiple multiple multiple
accept · 属性 accept allowedContentTypes mimeTypes accept accept
maxSize · 属性 maxSize maxSize maxSize maxSize maxSize
maxFiles · 属性 maxFiles maxFiles maxFiles maxFiles maxFiles
label · 属性 label label contentDescription semanticLabel accessibilityLabel
constraintsText · 属性 constraintsText constraintsText constraintsText constraintsText constraintsText
dropText · 属性 dropText dropText dropText dropText dropText
selectionText · 属性 selectionText selectionText selectionText selectionText selectionText
disabled · 属性 disabled disabled enabled enabled disabled
error · 属性 error isError isError isError error
required · 属性 required isRequired required required required
select · 事件 select onSelect onSelect onSelect onSelect
reject · 事件 reject onReject onReject onReject onReject
备注 The shadow-root form: a control whose parts and states are its own. <tanrei-file-picker accept=".json" constraints-text="…"><tanrei-button slot="trigger" variant="outlined">Choose a file</tanrei-button></tanrei-file-picker>. Attributes are kebab-case (max-size, max-files, constraints-text, drop-text, selection-text); accept is a comma-separated list, the form a native file input takes. The trigger and the glyph stay in the product's light DOM (the trigger and icon slots); the area and the texts are in the shadow root. The picker is a real <input type="file"> in the shadow root — hidden from sight, from the focus order and from assistive technology — carrying multiple and accept, whose click() the trigger's click and a press on the area call (both are user activations); its change and the area's drop feed FPK-9, and its value is cleared after each choice so that the same file can be chosen twice. The area listens for dragenter, dragover, dragleave and drop on itself only, counts enters and leaves so that a child does not end the condition, sets dropEffect to copy or none, and during a drag knows what the browser tells: kinds and a count (DataTransfer.items), never names or sizes, so a file that is too large is refused at the drop. select and reject are CustomEvents that bubble: detail.files is a File[], and reject's detail.files is { file, reason }[]. judge(files: Iterable<File>): { accepted: File[], refused: { file: File, reason: 'type' | 'size' | 'count' }[] } is FPK-31 and dispatches nothing. **Not form-associated**: no name, no value, no validity (FPK-7). It is a Field's control: it asks for the Field's connection as every Tanrei control does and takes the name, the description and the three states as values; a component that holds it gives the same values through the same connection. The description, aria-invalid and disabled are written on the slotted trigger's host — which a tanrei-button carries to its native button — and taken back when the trigger leaves the slot; the group's role and name are on an element of the shadow root, present only while there is a name (FPK-17). A button's role has no required state, so required is said only by a Field's required text in that description. Parts: container, icon, label, supporting-text, selection-text. Custom properties: --<namespace>-comp-file-picker-…. Without script, and until the element is defined, the trigger is a button that opens nothing. Class TanreiFilePicker. React: FilePicker, with onSelect, onReject, and a ref with judge(). TanreiFilePicker(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 view holds none after onSelect returns (FPK-7). 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; a Field's name, description and states arrive through the environment, and a view that holds the picker sets the same environment values. FPK-31 is TanreiFilePicker.judge(_:accept:maxSize:maxFiles:multiple:), a static function of the limits, since a view has no instance to call. No form. TanreiFilePicker(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 }, set only while there is a name. FPK-31 is rememberTanreiFilePickerState().judge(uris), or the top-level judgeFiles(uris, limits) it calls. No form. The framework has no file picker and no drop target for files; both are plugins, and the package depends on none. TanreiFilePicker(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 picker judges them by FPK-9 and emits. Drops arrive the same way: the product's drop plugin calls TanreiFilePickerController.dragEntered / dragExited / dropped(files), and without it the drop-area layout is drawn and pressed and takes no drop — the README says so. TanreiFilePickerController.judge(files) is FPK-31. disabled is the inverse flag enabled. No form: it is no FormField. React Native has no file picker of its own; the package depends on no picker library. <FilePicker 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 picker judges them by FPK-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. A ref holds judge(files) (FPK-31). No form.

令牌

令牌 引用 解析值(浅色)
--tanrei-comp-file-picker-container-gap sys.space.space150 12px
--tanrei-comp-file-picker-container-padding-block sys.space.space400 32px
--tanrei-comp-file-picker-container-padding-inline sys.space.space200 16px
--tanrei-comp-file-picker-container-shape sys.shape.corner.medium 8px
--tanrei-comp-file-picker-disabled-opacity — 1
--tanrei-comp-file-picker-focused-opacity — 1
--tanrei-comp-file-picker-hovered-opacity — 1
--tanrei-comp-file-picker-icon-size sys.icon.large 32px
--tanrei-comp-file-picker-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-file-picker-motion-state-duration sys.motion.role.state.large 250ms
--tanrei-comp-file-picker-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-file-picker-outline-dash-length sys.space.space50 4px
--tanrei-comp-file-picker-outline-style — 0
--tanrei-comp-file-picker-outline-width sys.border.hairline 1px
--tanrei-comp-file-picker-pressed-opacity — 1
--tanrei-comp-file-picker-receiving-container-color sys.color.secondary-container #eeeeee
--tanrei-comp-file-picker-receiving-content-color sys.color.on-secondary-container #262626
--tanrei-comp-file-picker-receiving-outline-color sys.color.on-secondary-container #262626
--tanrei-comp-file-picker-refusing-icon-color sys.color.error #cd262f
--tanrei-comp-file-picker-refusing-outline-color sys.color.error #cd262f
--tanrei-comp-file-picker-standard-container-color — #000000 · 0%
--tanrei-comp-file-picker-standard-disabled-container-color — #1b1b1b · 12%
--tanrei-comp-file-picker-standard-disabled-error-outline-color — #1b1b1b · 38%
--tanrei-comp-file-picker-standard-disabled-icon-color — #1b1b1b · 38%
--tanrei-comp-file-picker-standard-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-file-picker-standard-disabled-outline-color — #848484
--tanrei-comp-file-picker-standard-disabled-selection-text-color — #1b1b1b · 38%
--tanrei-comp-file-picker-standard-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-file-picker-standard-disabled-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-file-picker-standard-error-outline-color sys.color.error #cd262f
--tanrei-comp-file-picker-standard-focused-container-color — #1b1b1b · 10%
--tanrei-comp-file-picker-standard-focused-error-outline-color — #cd262f
--tanrei-comp-file-picker-standard-focused-icon-color — #4b4b4b
--tanrei-comp-file-picker-standard-focused-label-color — #1b1b1b
--tanrei-comp-file-picker-standard-focused-outline-color — #848484
--tanrei-comp-file-picker-standard-focused-selection-text-color — #4b4b4b
--tanrei-comp-file-picker-standard-focused-state-layer-color — #1b1b1b
--tanrei-comp-file-picker-standard-focused-supporting-text-color — #4b4b4b
--tanrei-comp-file-picker-standard-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-file-picker-standard-hovered-error-outline-color — #cd262f
--tanrei-comp-file-picker-standard-hovered-icon-color — #4b4b4b
--tanrei-comp-file-picker-standard-hovered-label-color — #1b1b1b
--tanrei-comp-file-picker-standard-hovered-outline-color — #848484
--tanrei-comp-file-picker-standard-hovered-selection-text-color — #4b4b4b
--tanrei-comp-file-picker-standard-hovered-state-layer-color — #1b1b1b
--tanrei-comp-file-picker-standard-hovered-supporting-text-color — #4b4b4b
--tanrei-comp-file-picker-standard-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-picker-standard-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-file-picker-standard-outline-color sys.color.outline #848484
--tanrei-comp-file-picker-standard-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-file-picker-standard-pressed-error-outline-color — #cd262f
--tanrei-comp-file-picker-standard-pressed-icon-color — #4b4b4b
--tanrei-comp-file-picker-standard-pressed-label-color — #1b1b1b
--tanrei-comp-file-picker-standard-pressed-outline-color — #848484
--tanrei-comp-file-picker-standard-pressed-selection-text-color — #4b4b4b
--tanrei-comp-file-picker-standard-pressed-state-layer-color — #1b1b1b
--tanrei-comp-file-picker-standard-pressed-supporting-text-color — #4b4b4b
--tanrei-comp-file-picker-standard-selection-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-picker-standard-state-layer-color comp.file-picker.standard.label.color #1b1b1b
--tanrei-comp-file-picker-standard-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-file-picker-supporting-text-gap sys.space.space100 8px
--tanrei-comp-file-picker-trigger-gap sys.space.space150 12px
--tanrei-comp-file-picker-label-typescale sys.typescale.body 400 14px/1.5714
--tanrei-comp-file-picker-selection-text-typescale sys.typescale.body 400 14px/1.5714
--tanrei-comp-file-picker-supporting-text-typescale sys.typescale.body-small 400 13px/1.5385

测试样例

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

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

指南

两种布局

布局 用在哪里 它绘制什么
button(默认) 还有其他字段的表单、狭窄的位置、手机、工具栏。 触发器、它旁边说明已选内容的文本,以及下方的限制。它不接收拖放。
drop-area 以交付文件为主要任务、并且用户多半使用指针的界面。 围着同一个触发器的一块有边界的区域,带有一个字形和一句提示。它接收拖到上面的文件,按下它的任何位置都会打开选择器。

两种布局都通过同一个按钮打开同一个选择器。拖动是附加的途径,绝不是唯一的途径:用键盘、触摸和辅助技术选择文件,靠的都是触发器。

触发器

触发器是你自己的按钮,标签、变体和尺寸都由你决定。在 trigger 插槽里恰好放一个。文件选择器赋予它行为,绝不改动它的样式。

  • 在拖放区域布局中,没有触发器的文件选择器仍然接收拖放,调试构建会把它报告为失败。
  • 除了触发器和 icon 插槽里的字形,你放在里面的其他任何东西都不会绘制。调试构建会报告这一点。
  • icon 插槽替换拖放区域顶部的上传字形:照片用图片字形,电子表格用表格字形。它是装饰性的,在按钮布局中会被忽略。

已选的内容

selectionText 是按钮布局中触发器旁边的文本:先是“未选择文件”,然后是“brief.pdf”,再然后是“3 个文件”。文件选择器不保留任何文件,所以写不出这段文本。它由保留文件的一方来写:你,或者文件上传,后者会替你填好。

让它留空,那个位置就什么都不绘制,这适合文件显示在别处的添加附件按钮。

说明限制

accept、maxSize、maxFiles 和 multiple 负责执行限制。constraintsText 负责说明限制。两者都要给出。

属性 限制 超出限制的文件
accept 接受的种类:带点的文件扩展名(.pdf)和媒体类型,整个类别写作 image/*。 以 type 为原因被拒绝。文件夹也以 type 为原因被拒绝。
maxSize 单个文件的最大大小,以字节为单位。 以 size 为原因被拒绝。
multiple 一次选择能否带来多个文件。 没有它时,同时到来的几个文件全部以 count 为原因被拒绝。文件选择器不会替用户从中挑一个。
maxFiles 设置 multiple 时,一次选择或一次拖放最多能带来的文件数。 超出的文件以 count 为原因被拒绝。设为 0 则拒绝所有文件。
  • 把限制写成用户一读就懂的话:“PDF 或 PNG,每个不超过 10 MB”。Tanrei 不会根据 accept 或字节数拼出句子,因为它不自带任何文字。
  • 限制绘制在文件选择器里,而不是字段的帮助文本里。字段的帮助文本会让位给错误文本,而那恰恰是用户需要再读一遍限制的时候。
  • 每次选择,select 带着被接受的文件触发一次,随后 reject 带着每个被拒绝的文件及其原因触发一次。对于拒绝,文件选择器什么都不绘制,也什么都不说。请用你自己的话说明发生了什么:用字段的错误文本、你的列表中的一行,或者一条由你朗读的消息。
  • 限制针对的是一次选择。文件选择器不知道你此前保留了什么:请把 maxFiles 设为还能再放的文件数,或者由你自己判定总数。文件上传会做这件事。
  • 有东西拖在区域上方时,浏览器只告知种类和数量,绝不告知名称和大小。因此,过大的文件是在放下时被拒绝的,而不是还拖着的时候。

在字段里

表单要显示标签、帮助文本、错误或必填标记时,把文件选择器放进字段。字段绘制它们,并把它们交给文件选择器,文件选择器的触发器随即由它们命名和描述。

<tanrei-field label="附件" required required-text="必填"
              error error-text="请选择不超过 10 MB 的 PDF。">
  <tanrei-file-picker accept=".pdf" max-size="10000000"
                      constraints-text="PDF,不超过 10 MB" selection-text="未选择文件">
    <tanrei-button slot="trigger" variant="outlined">选择文件</tanrei-button>
  </tanrei-file-picker>
</tanrei-field>

文件选择器不提交任何东西。它没有 name,也没有值,required 只是告知辅助技术。是否缺少文件,要由保留文件的组件来说:你的代码,或者文件上传,后者是一个表单控件。required(字段的说法)与 required 相同。

从右到左

在从右到左的布局中,触发器位于右侧,已选内容的文本在它左边,限制从右边缘开始。拖放区域的那一列仍然居中。作为 selectionText 给出的文件名保持自己的顺序,连同它的点和扩展名。字形只在图标集有相应标记时才镜像。

推荐

  • 立即把 select 交给你的东西保留下来。
  • 说明选了什么:用 selectionText,或者在你的产品显示文件的地方说明。
  • 每一次拒绝都要在用户正看着的地方用文字说明。
  • 文件列在选择器下方、并随表单发送时,使用文件上传。

避免

  • 不要把拖放区域当作选择文件的唯一途径。它里面始终有触发器。
  • 不要指望表单提交选中的内容。文件选择器不参与表单。
  • 不要为了这一处而深入触发器内部改动它的样式。它是你的按钮:请选择它的变体和尺寸。
  • 不要在它里面搭建文件列表。请把文件项放在它下方,或者使用文件上传。

无障碍

角色
group
标注
A group named by label, or by the label of the Field around it; with neither, no group — the trigger alone, a button named by its own label. The trigger is described by constraintsText, by selectionText while it is drawn, and by what the Field or the component that holds the file picker gives; it is exposed as required, invalid and disabled as the props say. The drop area and its glyph are not exposed; dropText is read as text.
最小目标
即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px

键盘操作

按键 动作
Tab / Shift+Tab Moves focus to the trigger. The drop area is not a stop.
Enter / Space On the trigger: opens the platform's file picker, as the Button's own activation.
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.

备注

  • Dragging is never the only way (WCAG 2.5.7): the trigger opens the same picker in both layouts and is the path for keyboard, touch, switch and voice. A drop area without a trigger is reported as a functional failure (FPK-5). The area adds a larger pointer target and takes drops; it adds no role, no stop and no key (FPK-12).
  • Without script, and until the component is defined, on the web: the trigger is a button that opens nothing. A custom element cannot open the platform's picker by markup alone, and the file picker 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.
  • The limits are words before the choice (WCAG 3.3.2): constraintsText is drawn and is the trigger's description. accept, maxSize and maxFiles enforce what it says; a product that sets them and writes no words is told so in debug builds (FPK-18).
  • A refused file must be said (WCAG 3.3.1), and only the product has the words and the place. On reject the product sets the errorText of the Field around the file picker, marks the file in a list of its own, or announces a sentence through a live region of its own. The file picker draws and says nothing for a refusal by itself, because it has no sentence for it and no row to put one on; the component built from it that keeps files draws such a row.
  • What is dragged over the area is the pointer user's own act and is not announced. The conditions are told without color too: receiving by its fill and, under forced colors, a thicker solid edge; refusing by the block glyph (FPK-13, FPK-23).
  • The file picker has no live region (FPK-17): it holds nothing whose change it could say. What was chosen is said by whoever shows it — the text beside the trigger, which is part of the trigger's description, or a list the product draws.
  • A file picker alone is no form control (FPK-7). It keeps nothing, so a value it submitted would be a file nobody can see or take back, and a required check would ask for something it cannot hold. A plain form takes the component that keeps the files and lists them; a product that draws its own list holds the files and sends them itself, or gives them to a control of its own.
  • required is a state for assistive technology alone, and a button has none on the web: there it is said by the Field's required text in the trigger's description, as for every control whose role has no required state.

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

组件负责的部分

  • 触发器是你的按钮。它的描述来自限制和说明已选内容的文本;在字段里,还来自字段的帮助文本或错误文本。
  • 有 label 时,或者在字段里,文件选择器是一个以它命名的组。没有名称时,它不暴露任何组:工具栏里的添加附件按钮只是一个按钮。
  • 拖放区域是同一个按钮的一个大的指针目标。它没有角色,没有名称,在焦点顺序中没有停靠点,也不响应任何按键。
  • error 把触发器暴露为无效,并用错误颜色绘制区域的边线。disabled 禁用触发器,区域也不再响应任何操作。
  • 文件选择器什么都不朗读。它没有实时区域:选了什么、拒绝了什么,由保留文件的一方来说。
  • 文本放大到 200% 时,所有文本都会换行,没有任何内容被截断。在强制颜色模式下,区域保留一道边线,正在接收拖动的区域边线更粗。

它在 Web 上的构造方式:选择器是 shadow root 里一个真正的 <input type="file">,它看不见,不在焦点顺序中,也对辅助技术隐藏。触发器和字形留在你的 DOM 里。select 和 reject 会冒泡;detail.files 是一个 File[],在 reject 中则是一个由 { file, reason } 组成的列表。

Web 上的已知差距:

  • 按钮没有必填状态。在字段里,字段的必填文字会加入触发器的描述。
  • 区域拒绝拖动时,浏览器不会把放下的内容交给它。这时,只有放下之前无法得知的情况(例如文件过大)才会触发 reject。
  • 在 Safari 中,点击不会让按钮获得焦点。在那里用指针选择文件之后,焦点仍在原来的位置。
  • 虚线边线无法贴合平滑过的圆角。在使用圆角平滑的设计系统中,浏览器绘制这种曲线的地方,边线画成实线。
  • 没有脚本时,以及在元素完成定义之前,触发器是一个什么都打不开的按钮。必须在不运行脚本的地方接收文件的页面,要写一个原生的 <input type="file">。

需要你做的事

  • 在 trigger 插槽里放一个按钮,它的标签要说明它做什么。
  • 只要文件选择器不只是一个孤零零的按钮,就给它一个 label,或者把它放进字段。
  • 只要设置了 accept、maxSize 或 maxFiles,就要给出 constraintsText。
  • 用文字说明选了什么,也用文字说明每一次拒绝。
  • 文件出现在用户没有看着的地方时,在你自己的实时区域里朗读添加了什么。