{
  "slug": "file-upload",
  "schemaVersion": 2,
  "sourceKind": "structured-spec",
  "confidence": "high",
  "implementation": "workbench.css",
  "behavior": "workbench.js",
  "preview": "preview/workbench-files.html",
  "reference": "preview/workbench-files.html",
  "specification": "WORKBENCH.md §15.1",
  "intent": "文件上传与拖放：拖入多个文件或整个文件夹，也可以用按钮选择；逐个文件显示检查结果、进度、失败原因和重试",
  "api": "initDropZone(zone, { accept, maxSize, onFiles }) → { open(folder) }；checkFile(file, { accept, maxSize }) / formatBytes(n)",
  "variantDimensions": [
    {
      "axis": "source",
      "values": [
        "files",
        "folder",
        "zip"
      ]
    },
    {
      "axis": "row-state",
      "values": [
        "waiting",
        "uploading",
        "done",
        "failed",
        "rejected"
      ]
    }
  ],
  "anatomy": {
    "parts": [
      "drop-zone",
      "icon",
      "title",
      "hint",
      "pick-files",
      "pick-folder",
      "status",
      "file-list",
      "file-row",
      "file-meta",
      "progress",
      "risk",
      "row-action"
    ],
    "required": [
      "drop-zone",
      "pick-files",
      "file-list",
      "file-row"
    ]
  },
  "markup": {
    "zone": "<div class=\"wb-drop\">[upload 图标]<strong>把文件或文件夹拖到这里</strong><span>支持 PDF、STEP、DXF、DWG 和 ZIP，单个不超过 200 MB</span><div class=\"wb-drop-actions\"><button type=\"button\" class=\"wb-btn sm\" data-drop=\"files\">选择文件</button><button type=\"button\" class=\"wb-btn sm\" data-drop=\"folder\">选择文件夹</button></div><input type=\"file\" multiple hidden><input type=\"file\" webkitdirectory hidden></div>",
    "row": "<li class=\"wb-file\">[file 图标]<div><span class=\"wb-file-name\">外壳总成.step</span><span class=\"wb-file-meta\">18 MB</span></div><div class=\"wb-file-state\"><span>上传中 42%</span><span class=\"wb-progress\" role=\"progressbar\" aria-valuenow=\"42\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-label=\"外壳总成.step 上传进度\"><span style=\"width:42%\"></span></span></div><div class=\"wb-file-actions\"><button type=\"button\" class=\"wb-btn sm ghost\">取消</button></div></li>",
    "failed": "<div class=\"wb-file-state\"><span class=\"wb-risk\">[error 图标]上传失败：网络中断</span></div><div class=\"wb-file-actions\"><button type=\"button\" class=\"wb-btn sm\">[refresh 图标]重试</button></div>"
  },
  "rules": [
    "拖放区写明接受的类型和单个文件的大小上限；类型用扩展名列出，不写「支持多种格式」",
    "拖放之外必须有「选择文件」按钮，需要整个文件夹时再加「选择文件夹」；这两个按钮是键盘和读屏的路径",
    "拖入时拖放区换成次级底色、描边加深；不用蓝色，不用动画",
    "每个文件单独一行，先检查类型、大小和空文件，未通过的写明原因并可移除，不影响其他文件",
    "上传中显示百分比和 4px 墨色进度条，可取消；失败写明原因并提供「重试」，重试只重传这一个文件",
    "文件夹按相对路径列出其中的文件；压缩包按一个文件上传，由服务端解压后再按文件列出",
    "检查结果和整批结果用 role=\"status\" 播报一次，例如「已加入 5 个文件，1 个未通过检查」",
    "上传与识别是两个状态：「已上传」之后的识别进度写在同一行，不混成一个状态"
  ],
  "states": [
    "idle",
    "drag-over",
    "uploading",
    "done",
    "failed",
    "rejected",
    "cancelled"
  ],
  "keyboard": [
    "Tab 到「选择文件」「选择文件夹」，Enter 或空格打开系统文件选择框",
    "行内的取消、重试、移除是普通按钮，按 Tab 顺序到达"
  ],
  "accessibility": [
    "进度条 role=\"progressbar\"，带 aria-valuenow 和包含文件名的可访问名",
    "失败和未通过检查的原因是文字，不只靠颜色或图标",
    "拖放区本身不可聚焦，键盘用户只经过其中的按钮"
  ],
  "doNotInvent": [
    "只能拖放、没有选择按钮的上传区",
    "蓝色或彩色的拖入高亮、跳动的上传图标",
    "整批失败时清空全部已选文件",
    "在前端解压压缩包或解析 CAD 文件",
    "用提示条报告单个文件的失败（失败写在该文件的行内）"
  ]
}
