FileUpload 文件上传
选择文件、拖放文件,并列出已选与已上传的文件。
用法
投放区自身就是一个大按钮,隐藏输入是必备部件,缺少它无法选择文件
组件结构
加粗的是必需部件。
data-scope="file-upload":root · label · dropzone · trigger · hidden-input · list · item · item-name · item-link · item-size-text · item-preview · item-progress · item-delete-trigger · clear-trigger
示例
限制与拒收
accept / maxFiles / maxFileSize 越界的当场被拒绝,file-reject 逐个报告理由
受控
传入 files 后由宿主决定,组件自身不再落值,只发 files-change 报告意图
禁用
disabled 把投放区、触发器与隐藏输入一并关停,拖拽进入也不再接收
预置列表
defaultFiles 给出挂载时已存在的文件,之后列表照常由组件自行保管,删除与清空都照常
选择整个目录
directory 使隐藏输入改为接收目录,选中目录下的文件一次性全部进入,数量上限要随之放开
缩略图墙
item-preview 是一个空方框,作者可放置任意内容;放入的图片会被裁为方格,一行排几张由外层网格决定
作者的准入判定
accept 与大小数量之外的规矩交给 validate:类型与大小通过之后逐个问它,返回拒绝码即拒收,拒收的文件带着这个码进 file-reject;这里同名文件只收最先到的一份
上传生命周期
提供一个 upload 实现后组件即为上传器:接收即开始上传(auto-upload 可关闭为手动),进度、成败与返回地址都在每条的传输快照中,失败可一键重试
试试选一个文件名带「坏」字的文件,看失败与重试。
列表项上的下载
条目中放置什么由作者决定:一条普通的 a[download] 就是下载入口;需要自行接管时换为按钮,在处理器中自行获取
服务器附件回显
remote-files 承载编辑表单中已存在的附件:与本地文件同列渲染(allFiles 远程在前)、占用 max-files 名额,删除经 remote-files-change 由宿主落库
并发上限与取消
max-concurrent-uploads 限定同时在传的份数,多出来的排队依次补上;cancelUpload 只中止传输、文件留在列表里,startUpload 让它重新开传
设计指引
何时使用
- 需要用户提交文件的场景。
- 需要预览、限制类型与大小,或选择整个目录。
何时不用
- 只展示已有附件、不允许新增时,使用列表。
特性
- 超出
maxFiles/maxFileSize/minFileSize的文件立即被拒绝,onFileReject逐个报告原因。 autoUpload决定选择后立即上传还是等待提交。remoteFiles回显服务器上已有的附件,与本次新选的文件并列在同一个列表中。validate在类型与大小校验之后逐个判定,返回拒绝码即拒收,与内建原因一起进onFileReject。- 焦点在组件里时可以直接粘贴文件(
allowPaste,默认开启),与选择、投放走同一道校验。 - 上传生命周期(完成、失败、取消)各有回调;
maxConcurrentUploads限定同时在传的份数,其余排队。 readOnly时列表改不动(选择、拖放、粘贴、删除与清空都不收),已经开始的传输照常;required时标题画星号,列表为空时文件输入带原生required。cancelUpload中止传输但保留文件,startUpload让取消或失败的文件重新开传;删除文件同样中止它的传输。upload返回{ id }时,传完的文件转成服务器已有附件:从files移出、追加进remoteFiles,表单值直接是附件记录。remoteFiles受控时宿主写回含这个 id 的列表之后才移出。- 点标题只把焦点交给投放区(没写投放区时交给选择钮),不弹出选择框;
label的for仍指向隐藏输入,原生关联与必填提示照旧。禁用时投放区退出 Tab 序列,只读时留在 Tab 序列里,两者都报aria-disabled、不收拖放与粘贴。 - 不写结构时按标准结构铺出整套部件(Vue 不写默认插槽、React 不给 children、Web Components 不写部件或只写
root),三端出自同一个结构函数:label给标题文字,接着是投放区(说明文字取translations.dropzone)、表单影子与文件列表,clearable时末尾带清空钮。投放区是唯一可聚焦的打开入口,不另铺选择钮;列表里allFiles每个文件一行(缩略图、文件名、大小、进度与删除钮),按fileKey认行,删掉中间一行时替身留在原处淡出。远程附件的url经安全判定(只放行 http、https、mailto、tel 与相对地址)后,文件名铺成链接item-link,打开方式取linkTarget(缺省当前页,_blank时带rel="noopener noreferrer");地址不安全时文件名是纯文字。只读时链接照常可聚焦、可打开,禁用时退出 Tab 序列。Web Components 里标题与投放区的内容可以写在宿主的直接子节点slot="label"、slot="dropzone"上接管。
组合
最佳实践
- 在界面上说明允许的类型与大小上限,不等用户选择后才拒绝。
- 拒绝时说明是哪个文件、原因是什么。
反模式
- 只在前端校验,不在后端校验。
- 上传中不显示进度也不能取消。
API 参考
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-file-upload> |
| Vue 组件 | XhFileUploadClearTrigger XhFileUploadDropzone XhFileUploadHiddenInput XhFileUploadItem XhFileUploadItemDeleteTrigger XhFileUploadItemLink XhFileUploadItemName XhFileUploadItemPreview XhFileUploadItemProgress XhFileUploadItemSizeText XhFileUploadLabel XhFileUploadList XhFileUploadRoot XhFileUploadTrigger |
| 组合式函数 | useFileUpload |
| 状态机 | fileUploadMachine |
| 皮肤 | @xihan-ui/styles/file-upload.css |
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
labelled | boolean | 作者渲染了 label 部件时置真,由适配器统计而不是判断标题文字是否有值。 为假时可聚焦部件不把 label 的 id 写进 aria-labelledby:指向未渲染的 id 是无效引用,作者写在控件上的 aria-label 也会被它压住。 | |
ariaLabel | string | 不显示出来的名字,写成 aria-label 落在与字段标签同一个部件上。 放进字段(字段接线)或渲染了 label 部件时名字以它们为准,aria-labelledby 盖过它。 | |
accept | string | string[] | 允许的类型,写法与原生 input 的 accept 一致: 接受 'image/*' 这类通配、'.png' 这类扩展名、'application/pdf' 这类精确 MIME, 逗号分隔的整串或数组两种形态均可(属性只能表达整串,数组需通过 property)。 | |
maxFiles | number | 最多保留的文件数,默认 1。提供 Infinity 即不限。 | |
maxFileSize | number | 单个文件的字节上限,默认不限。 | |
minFileSize | number | 单个文件的字节下限,默认 0(拦截 0 字节的空文件可设为 1)。 | |
disabled | boolean | ||
invalid | boolean | 校验失败标注;只作用于样式与 data-invalid,不阻断接收文件。 | |
readOnly | boolean | 只读:列表改不动——选择、拖放、粘贴、删除与清空都不收,已经开始的传输照常进行。 投放区与三种按钮仍可聚焦,报 aria-disabled="true"。 | |
required | boolean | 必填:根与标题写 data-required(标题据此画星号);列表为空时文件输入带原生 required,拦下空表单的提交。 有文件时不带:选完文件输入就被清空,恒带 required 会把已经选了文件的表单也拦下。 | |
name | string | 表单字段名;提供后隐藏输入才参与提交。 | |
files | File[] | 已选文件。提供即受控:cell 直读 prop,写入只发 onFilesChange 不落内部值。 | |
defaultFiles | File[] | ||
allowDrop | boolean | 是否接受拖拽投放,默认 true。关闭后投放区不再拦截默认行为,也不再输出 data-dragging。 | |
allowPaste | boolean | 是否接受粘贴,默认 true:焦点在组件里(投放区、选择钮、删除钮)时 Ctrl / Cmd+V 收下剪贴板里的文件, 与选择、投放走同一道校验。剪贴板里没有文件时不拦截,文字照常粘贴到别处。 | |
validate | (file: File, context: FileUploadValidateContext) => string | string[] | null | undefined | 作者自己的准入判定,在类型与大小校验通过之后、数量上限之前逐个调用:返回拒绝码(一个或一组)即拒收, 拒收的文件连同返回的码一起进 onFileReject,不占数量名额;返回 null / undefined / 空数组即放行。 只接受同步判定;要读图片尺寸之类的异步检查放进 upload,失败时抛错走 onUploadError。 | |
maxConcurrentUploads | number | 同时在传的文件数上限,默认不限。到了上限的文件报 queued 排队,前面的传完、失败或被取消后按列表顺序补上。 | |
directory | boolean | 选择目录而不是文件(隐藏输入带 webkitdirectory)。 | |
capture | 'user' | 'environment' | 移动端直接调用摄像头 / 麦克风采集。 | |
linkTarget | string | 附件链接(item-link)的打开方式,写到链接的 target 上;缺省在当前页打开。 写 _blank 时链接同时带 rel="noopener noreferrer"。 | |
remoteFiles | FileUploadRemoteFile[] | 服务器已有附件(编辑表单回显)。提供即受控:cell 直读 prop,删改只发 onRemoteFilesChange 不落内部值。条目计入 maxFiles 总量,与本地文件一起渲染。 | |
defaultRemoteFiles | FileUploadRemoteFile[] | ||
upload | (request: FileUploadRequest) => Promise<FileUploadResult | undefined | void> | FileUploadResult | undefined | void | 每个文件的传输实现。提供后组件才是上传器:接受的文件按 autoUpload 自动开始传输, 进度、成败与返回地址都记入该文件的传输快照。未提供时保持纯选择器。 | |
autoUpload | boolean | 接受后即自动开始传输,默认 true;关闭后由 api.startUpload 逐个开始。 | |
translations | Partial<FileUploadTranslations> | ||
onFilesChange | (details: FileUploadFilesChangeDetails) => void | 列表变化意图回调;受控时是唯一出口,非受控时随内部写入一并通知。 | |
onClear | () => void | 用户按清空钮(clear-trigger)清掉了值;先发值变化,再发它。程序化的 clear() 不发。 | |
onFileAccept | (details: FileUploadFileAcceptDetails) => void | 本次接受了哪些文件。受控与否都发出:宿主据此发起上传。 | |
onFileReject | (details: FileUploadFileRejectDetails) => void | 本次拒绝了哪些文件及各自的原因。 | |
onRemoteFilesChange | (details: FileUploadRemoteFilesChangeDetails) => void | 远程附件列表变化意图回调;受控时是唯一出口,非受控时随内部写入一并通知。 | |
onUploadComplete | (details: FileUploadCompleteDetails) => void | 单个文件传输完成(upload 的 Promise 兑现)。 | |
onUploadError | (details: FileUploadErrorDetails) => void | 单个文件传输失败(upload 的 Promise 拒绝);中止不视为失败,不发出。 | |
onUploadCancel | (details: FileUploadCancelDetails) => void | 单个文件的传输被 cancelUpload 取消(文件留在列表里);删除文件时的中止不发出。 |
FileUploadRemoteFile
remoteFiles 的元素。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 稳定标识(通常是服务端主键),删除与去重都以它为准。 |
name | string | 是 | |
size | number | 字节数;未提供时不显示大小。 | |
type | string | MIME 类型。 | |
url | string |
事件
自定义元素将载荷放在 detail;Vue 使用同名 emit。
| 事件 | 载荷 | 说明 |
|---|---|---|
files-change | FileUploadFilesChangeDetails | 列表变化;detail 为 { files: File[] } |
clear | `` | 用户按清空钮(clear-trigger)清掉了值;先发值变化,再发它。程序化的 clear() 不发。 |
remote-files-change | FileUploadRemoteFilesChangeDetails | 远程附件列表变化;detail 为 { files: FileUploadRemoteFile[] } |
upload-complete | FileUploadCompleteDetails | 单个文件传输完成;detail 为 { file, url?, id? },带 id 时这个文件随后转入 remoteFiles |
upload-error | FileUploadErrorDetails | 单个文件传输失败;detail 为 { file, error } |
upload-cancel | FileUploadCancelDetails | 单个文件的传输被 cancelUpload 取消(文件留在列表里);detail 为 { file } |
file-accept | FileUploadFileAcceptDetails | 本次接受了哪些文件;detail 为 { files: File[] } |
file-reject | FileUploadFileRejectDetails | 本次拒绝了哪些文件及各自的原因;detail 为 { files: { file, reasons }[] } |
插槽
仅列出带载荷的插槽。
| Vue 组件 | 插槽 | 载荷 | 说明 |
|---|---|---|---|
XhFileUploadRoot | default | FileUploadRootSlotProps | |
XhFileUploadRoot | label | — | |
XhFileUploadRoot | dropzone | — | 不写默认插槽时投放区里的内容;未写时取 translations.dropzone。 |
React 适配器 props
只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。
| React 组件 | 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
XhFileUploadItem | file | FileUploadFile | 该行显示哪个文件(本地或远程附件)。 | |
XhFileUploadItem | index | number | string | 改用下标从 allFiles(远程在前、本地在后)中取文件,兼收字符串。 | |
XhFileUploadRoot | label | ReactNode | 标题。不给 children 时铺 label 部件。 | |
XhFileUploadRoot | dropzone | ReactNode | 不给 children 时投放区里的内容;未写时取 translations.dropzone。 | |
XhFileUploadRoot | clearable | boolean | 不给 children 时列表之后带清空整份列表的按钮;给了 children 时放 clear-trigger 部件即可清空。 | |
XhFileUploadRoot | children | SlotChildren<FileUploadRootSlotProps> |
状态
公开状态写入 data-state。
| 部件 | 取值 |
|---|---|
item | uploadOf(file)?.status |
item-progress | upload?.status |
以下名称仅用于内部状态机。
状态:idle · dragging
事件:FILES.SET · FILES.ADD · FILE.DELETE · FILES.CLEAR · PICKER.OPEN · DRAG.OVER · DRAG.LEAVE · DROP · PASTE · UPLOAD.CANCEL · UPLOAD.START · REMOTE.DELETE · FORM.RESET · PRESS.START · PRESS.END · LIST.TRACKED · PROGRESS.SETTLED
判据:canChange · canTransfer · canDrop · canPaste
connect API
getXxxProps() 返回对应部件的宿主属性。
| 成员 | 类型 | 说明 |
|---|---|---|
acceptedFiles | File[] | |
remoteFiles | FileUploadRemoteFile[] | 服务器已有附件。 |
allFiles | FileUploadFile[] | 渲染顺序的完整列表:远程在前、本地在后。 |
dragging | boolean | 有内容正悬停在投放区上方。 |
disabled | boolean | |
invalid | boolean | |
readOnly | boolean | |
required | boolean | |
empty | boolean | 没有任何文件。清空按钮据此写 data-empty,空列表据此显示占位。 |
maxFiles | number | 生效的数量上限(已按默认值与非法值归一)。 |
translations | FileUploadTranslations | 合上作者覆盖后的文案。 |
fileKey | (file: FileUploadFile) => string | 文件在列表里的稳定标识:本地文件取机器发的内部 id,远程附件取 remote:<id>。 同名同大小的两份本地文件标识不同;列表增删时据此认出哪一行还是那一行。 |
fileHref | (file: FileUploadFile) => string | undefined | 文件可打开的地址:远程附件的 url 经安全判定(只放行 http、https、mailto、tel 与相对地址)后的结果; 本地文件、没有 url 或地址不安全时为 undefined,此时标准结构只铺纯文字的文件名。 |
getFileSizeText | (file: FileUploadFile) => string | 字节数格式化为可读形式,供作者渲染 item-size-text;远程附件未报大小时为空串。 |
uploadOf | (file: FileUploadFile) => FileUploadSnapshot | null | 该条目的传输快照:远程附件恒为 done;本地文件未配置 upload 时为 null, 已配置而尚未开始传输时为 idle。 |
startUpload | (file: File) => void | 手动开始传输(autoUpload 关闭时)、失败或取消后重试;不在列表中或传输中的文件调用无效。 |
cancelUpload | (file: File) => void | 取消这个文件的传输(在传或排队中),文件留在列表里、状态落 canceled;删除用 deleteFile。 |
setFiles | (files: File[]) => void | |
addFiles | (files: File[]) => void | |
deleteFile | (file: FileUploadFile) => void | 本地文件按引用移除(传输中会中止),远程附件按 id 移除。 |
clear | () => void | 清空整份列表(本地与远程一起)。 |
openFilePicker | () => void | |
getRootProps | () => T['element'] | |
getLabelProps | () => T['label'] | |
getDropzoneProps | () => T['element'] | |
getTriggerProps | () => T['button'] | |
getHiddenInputProps | () => T['input'] | |
getListProps | () => T['element'] | |
getItemProps | (props: FileUploadItemProps) => T['element'] | |
getItemNameProps | (props: FileUploadItemProps) => T['element'] | |
getItemLinkProps | (props: FileUploadItemProps) => T['element'] | 附件链接:落在文件名里、指向 fileHref 的 <a>。地址不安全时不写 href;target 取 linkTarget,_blank 时带 rel="noopener noreferrer"。只读时照常可聚焦、可打开;禁用时退出 Tab 序列、报 aria-disabled,点了不跳转。 |
getItemSizeTextProps | (props: FileUploadItemProps) => T['element'] | |
getItemPreviewProps | (props: FileUploadItemProps) => T['element'] | |
getItemProgressProps | (props: FileUploadItemProps) => T['element'] | 该条目的传输进度条,纯装饰;进度比例写在私有槽上供皮肤计算宽度。 |
getItemDeleteTriggerProps | (props: FileUploadItemProps) => T['button'] | |
getClearTriggerProps | () => T['button'] |
无障碍
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Tab / Shift+Tab | focus outside / inside the component | 投放区、选择按钮、每条的删除按钮与清空按钮各占一个 Tab 位;禁用时投放区退出 Tab 序列,几个原生按钮带 disabled 本就不可聚焦 |
Enter / Space | focus on dropzone | 打开系统文件选择框。投放区是 div,浏览器不会替它把这两个键合成成一次点击,连接层自己接管(并拦下空格滚屏) |
Enter / Space | focus on trigger | 打开系统文件选择框(原生 button 的默认激活) |
Enter / Space | focus on item-delete-trigger | 把这一条从列表里删掉(原生 button 的默认激活) |
Enter / Space | focus on clear-trigger | 清空整份列表(原生 button 的默认激活);列表为空时按钮照常在位、可聚焦,激活是空操作 |
Ctrl+V / Meta+V | focus inside the component, not disabled, allowPaste | 收下剪贴板里的文件,与选择、投放走同一道校验;剪贴板里没有文件时不拦截,文字照常粘贴到别处 |
Enter / Space | held on trigger / item-delete-trigger / clear-trigger, not disabled | 按住期间该按钮投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下(选择钮打开系统文件框即失焦),删除钮随文件离开列表时一并撤下 |
ARIA
以下属性由 connect 生成。
| 部件 | 属性 | 值 |
|---|---|---|
dropzone | aria-disabled | 'false' | 'true' |
dropzone | aria-invalid | 'true' | 'false' |
dropzone | aria-label | undefined | props.ariaLabel |
dropzone | aria-labelledby | label 部件的 id dropzone 部件的 id | dropzone 部件的 id |
dropzone | role | 'button' |
trigger | aria-disabled | 'true' | undefined |
hidden-input | aria-labelledby | label 部件的 id dropzone 部件的 id | dropzone 部件的 id |
list | role | 'list' |
item | role | 'listitem' |
item-link | aria-disabled | 'true' | undefined |
item-preview | aria-hidden | 'true' |
item-progress | aria-hidden | 'true' |
item-delete-trigger | aria-disabled | 'true' | undefined |
item-delete-trigger | aria-label | label.deleteItem(file) |
clear-trigger | aria-disabled | 'true' | undefined |
clear-trigger | aria-label | label.clearTrigger |
样式参考
皮肤
@xihan-ui/styles/file-upload.css 按 [data-scope="file-upload"][data-part="root"] 部件选择器书写,发布产物以挂载类 .xh-scope-file-upload 代替其中的 data-scope(特异性相同),位于 xihan.components 层。覆盖样式使用 xihan.overrides,部件选择器照常可用。
forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。
数据属性
由 connect 生成;条件不成立时不输出无值属性。
| 部件 | 属性 | 值 |
|---|---|---|
root | data-disabled | ''(条件成立时才出现) |
root | data-dragging | ''(条件成立时才出现) |
root | data-empty | ''(条件成立时才出现) |
root | data-invalid | ''(条件成立时才出现) |
root | data-readonly | ''(条件成立时才出现) |
root | data-required | ''(条件成立时才出现) |
label | data-disabled | ''(条件成立时才出现) |
label | data-required | ''(条件成立时才出现) |
dropzone | data-disabled | ''(条件成立时才出现) |
dropzone | data-dragging | ''(条件成立时才出现) |
dropzone | data-invalid | ''(条件成立时才出现) |
dropzone | data-readonly | ''(条件成立时才出现) |
trigger | data-disabled | ''(条件成立时才出现) |
trigger | data-pressed | ''(条件成立时才出现) |
trigger | data-readonly | ''(条件成立时才出现) |
trigger | data-xh-action-control | '' |
trigger | data-xh-action-display | 'always' |
trigger | data-xh-action-profile | 'text' |
trigger | data-xh-action-size | 'md' |
trigger | data-xh-action-variant | 'outline' |
list | data-disabled | ''(条件成立时才出现) |
list | data-empty | ''(条件成立时才出现) |
list | data-instant | ''(条件成立时才出现) |
item | data-disabled | ''(条件成立时才出现) |
item | data-file-name | file.name |
item | data-file-size | undefined | String(file.size) | String(file.size) |
item | data-remote | ''(条件成立时才出现) |
item | data-state | uploadOf(file)?.status |
item-name | data-disabled | ''(条件成立时才出现) |
item-link | data-disabled | ''(条件成立时才出现) |
item-size-text | data-disabled | ''(条件成立时才出现) |
item-size-text | data-file-size | undefined | String(file.size) | String(file.size) |
item-preview | data-disabled | ''(条件成立时才出现) |
item-preview | data-file-type | (isRemote(file) ? file.type ?? '' : file.type) || 'un… |
item-progress | data-disabled | ''(条件成立时才出现) |
item-progress | data-state | upload?.status |
item-delete-trigger | data-disabled | ''(条件成立时才出现) |
item-delete-trigger | data-pressed | ''(条件成立时才出现) |
item-delete-trigger | data-readonly | ''(条件成立时才出现) |
item-delete-trigger | data-xh-action-control | '' |
item-delete-trigger | data-xh-action-display | 'always' |
item-delete-trigger | data-xh-action-profile | 'icon' |
item-delete-trigger | data-xh-action-size | 'xs' |
item-delete-trigger | data-xh-action-variant | 'ghost' |
clear-trigger | data-disabled | ''(条件成立时才出现) |
clear-trigger | data-empty | ''(条件成立时才出现) |
clear-trigger | data-pressed | ''(条件成立时才出现) |
clear-trigger | data-readonly | ''(条件成立时才出现) |
clear-trigger | data-xh-action-control | '' |
clear-trigger | data-xh-action-display | 'always' |
clear-trigger | data-xh-action-profile | 'text' |
clear-trigger | data-xh-action-size | 'sm' |
clear-trigger | data-xh-action-variant | 'ghost' |
CSS 变量
本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。
| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
|---|---|---|---|---|---|
--xh-file-upload-clear-bg-active | clear-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | file-upload 的 clear-trigger 部件 background-color 覆盖槽。 |
--xh-file-upload-clear-bg-hover | clear-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | file-upload 的 clear-trigger 部件 background-color 覆盖槽。 |
--xh-file-upload-clear-fg | clear-trigger | color | default | --xh-fg-muted | file-upload 的 clear-trigger 部件 color 覆盖槽。 |
--xh-file-upload-clear-fg-hover | clear-trigger | color | disabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-fg-default | file-upload 的 clear-trigger 部件 color 覆盖槽。 |
--xh-file-upload-clear-font-size | clear-trigger | font-size | default | --xh-_action-profile-font-size | file-upload 的 clear-trigger 部件 font-size 覆盖槽。 |
--xh-file-upload-clear-gap | clear-trigger | gap | default | --xh-_action-profile-gap | file-upload 的 clear-trigger 部件 gap 覆盖槽。 |
--xh-file-upload-clear-h | clear-trigger | block-sizeinline-size | defaultxh-action-profile=icon | --xh-_action-profile-visual-size | file-upload 的 clear-trigger 部件 block-size、inline-size 覆盖槽。 |
--xh-file-upload-clear-px | clear-trigger | padding-inline | default | --xh-_action-profile-padding-inline | file-upload 的 clear-trigger 部件 padding-inline 覆盖槽。 |
--xh-file-upload-clear-radius | clear-trigger | border-radius | default | --xh-shape-control | file-upload 的 clear-trigger 部件 border-radius 覆盖槽。 |
--xh-file-upload-delete-bg-active | item-delete-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | file-upload 的 item-delete-trigger 部件 background-color 覆盖槽。 |
--xh-file-upload-delete-bg-hover | item-delete-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | file-upload 的 item-delete-trigger 部件 background-color 覆盖槽。 |
--xh-file-upload-delete-fg | item-delete-trigger | color | default | --xh-fg-muted | file-upload 的 item-delete-trigger 部件 color 覆盖槽。 |
--xh-file-upload-delete-fg-hover | item-delete-trigger | color | disabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-fg-danger-hover | file-upload 的 item-delete-trigger 部件 color 覆盖槽。 |
--xh-file-upload-delete-radius | item-delete-trigger | border-radius | default | --xh-shape-control | file-upload 的 item-delete-trigger 部件 border-radius 覆盖槽。 |
--xh-file-upload-delete-size | item-delete-trigger | block-sizeinline-size | defaultxh-action-profile=icon | --xh-_action-profile-visual-size | file-upload 的 item-delete-trigger 部件 block-size、inline-size 覆盖槽。 |
--xh-file-upload-dropzone-bg | dropzone | background | default | --xh-bg-subtle | file-upload 的 dropzone 部件 background 覆盖槽。 |
--xh-file-upload-dropzone-bg-disabled | dropzone | background | disabled | --xh-bg-subtle | file-upload 的 dropzone 部件 background 覆盖槽。 |
--xh-file-upload-dropzone-bg-dragging | dropzone | background | dragging | --xh-bg-subtle-hover | file-upload 的 dropzone 部件 background 覆盖槽。 |
--xh-file-upload-dropzone-bg-hover | dropzone | background | disableddragginghoverinvalidnot([data-disabled], [data-readonly], [data-invalid], [data-dragging])readonly | --xh-bg-subtle-hover | file-upload 的 dropzone 部件 background 覆盖槽。 |
--xh-file-upload-dropzone-border | dropzone | border | default | --xh-border-control | file-upload 的 dropzone 部件 border 覆盖槽。 |
--xh-file-upload-dropzone-border-dragging | dropzone | border-color | dragging | --xh-bg-brand | file-upload 的 dropzone 部件 border-color 覆盖槽。 |
--xh-file-upload-dropzone-border-focus | dropzone | border-color | disableddraggingfocus-visibleinvalidnot([data-disabled], [data-invalid], [data-dragging]) | --xh-_tone | file-upload 的 dropzone 部件 border-color 覆盖槽。 |
--xh-file-upload-dropzone-border-hover | dropzone | border-color | disableddragginghoverinvalidnot([data-disabled], [data-readonly], [data-invalid], [data-dragging])readonly | --xh-border-strong | file-upload 的 dropzone 部件 border-color 覆盖槽。 |
--xh-file-upload-dropzone-border-invalid | dropzone | border-color | invalid | --xh-border-invalid | file-upload 的 dropzone 部件 border-color 覆盖槽。 |
--xh-file-upload-dropzone-fg | dropzone | color | default | --xh-fg-default | file-upload 的 dropzone 部件 color 覆盖槽。 |
--xh-file-upload-dropzone-font-size | dropzone | font-size | default | --xh-text-body-size | file-upload 的 dropzone 部件 font-size 覆盖槽。 |
--xh-file-upload-dropzone-gap | dropzone | gap | default | --xh-space-2 | file-upload 的 dropzone 部件 gap 覆盖槽。 |
--xh-file-upload-dropzone-min-h | dropzone | min-block-size | default | --xh-dropzone-min-h | file-upload 的 dropzone 部件 min-block-size 覆盖槽。 |
--xh-file-upload-dropzone-px | dropzone | padding-inline | default | --xh-space-4 | file-upload 的 dropzone 部件 padding-inline 覆盖槽。 |
--xh-file-upload-dropzone-py | dropzone | padding-block | default | --xh-space-5 | file-upload 的 dropzone 部件 padding-block 覆盖槽。 |
--xh-file-upload-dropzone-radius | dropzone | border-radius | default | --xh-shape-control | file-upload 的 dropzone 部件 border-radius 覆盖槽。 |
--xh-file-upload-gap | labelroot | gapmargin-block-end | default | --xh-space-3 | file-upload 的 label、root 部件 gap、margin-block-end 覆盖槽。 |
--xh-file-upload-icon-size | root | --xh-icon-size | default | --xh-glyph-size-text | file-upload 的 root 部件 --xh-icon-size 覆盖槽。 |
--xh-file-upload-item-bg | item | background | default | --xh-bg-subtle | file-upload 的 item 部件 background 覆盖槽。 |
--xh-file-upload-item-bg-error | item | background | has(> [data-part='item-progress'][data-state='error'])state=closedstate=error | --xh-bg-surface | file-upload 的 item 部件 background 覆盖槽。 |
--xh-file-upload-item-border | item | border | default | transparent | file-upload 的 item 部件 border 覆盖槽。 |
--xh-file-upload-item-border-error | item | border-color | has(> [data-part='item-progress'][data-state='error'])state=closedstate=error | transparent | file-upload 的 item 部件 border-color 覆盖槽。 |
--xh-file-upload-item-fg | item | color | default | --xh-fg-default | file-upload 的 item 部件 color 覆盖槽。 |
--xh-file-upload-item-fg-done | item | background-color | has(> [data-part='item-progress'][data-state='done'])state=closedstate=done | --xh-fg-success | file-upload 的 item 部件 background-color 覆盖槽。 |
--xh-file-upload-item-fg-error | itemitem-name | background-colorcolor | has(> [data-part='item-progress'][data-state='error'])state=closedstate=error | --xh-fg-danger | file-upload 的 item、item-name 部件 background-color、color 覆盖槽。 |
--xh-file-upload-item-font-size | item | font-size | default | --xh-text-body-size | file-upload 的 item 部件 font-size 覆盖槽。 |
--xh-file-upload-item-gap | list | gap | default | --xh-space-3 | file-upload 的 list 部件 gap 覆盖槽。 |
--xh-file-upload-item-inner-gap | item | gap | default | --xh-space-3 | file-upload 的 item 部件 gap 覆盖槽。 |
--xh-file-upload-item-link-fg | item-link | color | default | --xh-fg-brand | file-upload 的 item-link 部件 color 覆盖槽。 |
--xh-file-upload-item-link-fg-hover | item-link | color | @media (hover: hover)disabledhovernot([data-disabled]) | --xh-fg-brand-strong | file-upload 的 item-link 部件 color 覆盖槽。 |
--xh-file-upload-item-link-radius | item-link | border-radius | default | --xh-shape-inset | file-upload 的 item-link 部件 border-radius 覆盖槽。 |
--xh-file-upload-item-link-underline-offset | item-link | text-underline-offset | default | --xh-space-0_5 | file-upload 的 item-link 部件 text-underline-offset 覆盖槽。 |
--xh-file-upload-item-mark-size | item | block-sizeinline-size | has(> [data-part='item-progress'][data-state='done'])has(> [data-part='item-progress'][data-state='error'])state=closedstate=donestate=error | --xh-control-indicator-size | file-upload 的 item 部件 block-size、inline-size 覆盖槽。 |
--xh-file-upload-item-name-min-w | item-name | min-inline-size | default | --xh-control-min-w | file-upload 的 item-name 部件 min-inline-size 覆盖槽。 |
--xh-file-upload-item-progress-fill | item-progress | background | default | --xh-bg-brand | file-upload 的 item-progress 部件 background 覆盖槽。 |
--xh-file-upload-item-progress-h | item-progress | block-size | default | --xh-stroke-thick | file-upload 的 item-progress 部件 block-size 覆盖槽。 |
--xh-file-upload-item-progress-radius | item-progress | border-radius | default | --xh-shape-pill | file-upload 的 item-progress 部件 border-radius 覆盖槽。 |
--xh-file-upload-item-progress-track | item-progress | background | default | --xh-bg-subtle | file-upload 的 item-progress 部件 background 覆盖槽。 |
--xh-file-upload-item-progress-w | item-progress | inline-size | default | --xh-control-h-md | file-upload 的 item-progress 部件 inline-size 覆盖槽。 |
--xh-file-upload-item-px | item | padding-inline | default | --xh-space-3 | file-upload 的 item 部件 padding-inline 覆盖槽。 |
--xh-file-upload-item-py | item | padding-block | default | --xh-space-2 | file-upload 的 item 部件 padding-block 覆盖槽。 |
--xh-file-upload-item-radius | item | border-radius | default | --xh-shape-control | file-upload 的 item 部件 border-radius 覆盖槽。 |
--xh-file-upload-label-fg | label | color | default | --xh-fg-muted | file-upload 的 label 部件 color 覆盖槽。 |
--xh-file-upload-label-fg-disabled | label | color | disabled | --xh-fg-muted | file-upload 的 label 部件 color 覆盖槽。 |
--xh-file-upload-label-font-size | label | font-size | default | --xh-text-label-size | file-upload 的 label 部件 font-size 覆盖槽。 |
--xh-file-upload-label-font-weight | label | font-weight | default | --xh-text-label-weight | file-upload 的 label 部件 font-weight 覆盖槽。 |
--xh-file-upload-label-gap | label | margin-block-end | default | --xh-space-2 | file-upload 的 label 部件 margin-block-end 覆盖槽。 |
--xh-file-upload-list-max-h | list | max-block-size | default | --xh-viewport-h-md | file-upload 的 list 部件 max-block-size 覆盖槽。 |
--xh-file-upload-preview-bg | item-preview | background | default | transparent | file-upload 的 item-preview 部件 background 覆盖槽。 |
--xh-file-upload-preview-fg | item-preview | color | default | --xh-fg-brand | file-upload 的 item-preview 部件 color 覆盖槽。 |
--xh-file-upload-preview-fg-image | item-preview | color | file-type=image/ | --xh-fg-brand | file-upload 的 item-preview 部件 color 覆盖槽。 |
--xh-file-upload-preview-radius | item-preview | border-radius | default | --xh-shape-control | file-upload 的 item-preview 部件 border-radius 覆盖槽。 |
--xh-file-upload-preview-size | item-preview | block-sizeinline-size | default | --xh-glyph-size-sm | file-upload 的 item-preview 部件 block-size、inline-size 覆盖槽。 |
--xh-file-upload-size-fg | item-size-text | color | default | --xh-fg-subtle | file-upload 的 item-size-text 部件 color 覆盖槽。 |
--xh-file-upload-size-font-size | item-size-text | font-size | default | --xh-text-caption-size | file-upload 的 item-size-text 部件 font-size 覆盖槽。 |
--xh-file-upload-trigger-bg | trigger | --xh-ink-surfacebackground-color | defaultxh-ink-surface | --xh-_action-variant-bg-rest | file-upload 的 trigger 部件 --xh-ink-surface、background-color 覆盖槽。 |
--xh-file-upload-trigger-bg-active | trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | file-upload 的 trigger 部件 background-color 覆盖槽。 |
--xh-file-upload-trigger-bg-hover | trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | file-upload 的 trigger 部件 background-color 覆盖槽。 |
--xh-file-upload-trigger-border | trigger | border | default | --xh-_action-variant-border-rest | file-upload 的 trigger 部件 border 覆盖槽。 |
--xh-file-upload-trigger-fg | trigger | color | defaultdisabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-fg-hover--xh-_action-variant-fg-pressed--xh-_action-variant-fg-rest | file-upload 的 trigger 部件 color 覆盖槽。 |
--xh-file-upload-trigger-font-size | trigger | font-size | default | --xh-_action-profile-font-size | file-upload 的 trigger 部件 font-size 覆盖槽。 |
--xh-file-upload-trigger-font-weight | trigger | font-weight | default | --xh-text-label-weight | file-upload 的 trigger 部件 font-weight 覆盖槽。 |
--xh-file-upload-trigger-gap | trigger | gap | default | --xh-_action-profile-gap | file-upload 的 trigger 部件 gap 覆盖槽。 |
--xh-file-upload-trigger-h | trigger | block-sizeinline-size | defaultxh-action-profile=icon | --xh-_action-profile-visual-size | file-upload 的 trigger 部件 block-size、inline-size 覆盖槽。 |
--xh-file-upload-trigger-px | trigger | padding-inline | default | --xh-_action-profile-padding-inline | file-upload 的 trigger 部件 padding-inline 覆盖槽。 |
--xh-file-upload-trigger-radius | trigger | border-radius | default | --xh-shape-control | file-upload 的 trigger 部件 border-radius 覆盖槽。 |
--xh-file-upload-trigger-shadow-hover | trigger | box-shadow | disabledhoverloadingnot([data-disabled])not([data-loading]) | none | file-upload 的 trigger 部件 box-shadow 覆盖槽。 |
动效
动效角色:按压 · 状态 · 指示与换位 · 出现 · 列表(见动效规范)。
共享关键帧 xh-fade-in · xh-fade-out · xh-item-in 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;background-color · border-color · color · opacity · translate 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。
系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。
响应式
皮肤另按输入能力分档:hover: hover:同一份皮肤在触屏与带指针的设备上不一样,与视口宽度无关。
RTL
皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像;只认物理方向的量乘 --xh-direction-sign 换向,按就近的 dir 走。
