File Dropzone
介绍
拖入或点击选择文件,拖拽悬停时边框高亮放大;每个文件有独立进度条,完成后打勾。附示例文件按钮。
纯 HTML、CSS 与少量原生 JS,没有框架依赖。复制提示词交给 Claude Code、Cursor、Trae 或 Codex,模型会把 File Dropzone 加进你的项目并换成你的配色。
适用场景
搜索框、AI 对话输入框、表单字段、文件上传和验证码等用户输入的地方。
安装
订阅 Builder 后,用 8pt CLI 装进项目,或在已接入 8pt MCP 的编辑器里直接调用。
$ npm i -g @8pt-dev/cli && 8pt add file-dropzone
mcp › 使用 8pt MCP 调用 get_component("file-dropzone")
代码
File Dropzone 的源码,动效只用 transform 与 opacity,并尊重减少动态效果设置。
<div class="x-drop">
<label class="x-drop__zone">
<input type="file" multiple hidden>
<span class="x-drop__ic" aria-hidden="true"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V6M6 11l6-6 6 6"/></svg></span>
<b>拖入文件,或点击选择</b>
<small>PNG、SVG、PDF,单个不超过 10MB</small>
</label>
<ul class="x-drop__list"></ul>
<button type="button" class="x-drop__demo">添加示例文件</button>
</div>.x-drop{display:grid;gap:8px;width:min(320px,100%);color:var(--d-fg,#fff)}
.x-drop__zone{display:grid;justify-items:center;gap:4px;padding:16px 12px;border-radius:14px;border:1.5px dashed var(--d-line,rgba(255,255,255,.2));background:var(--d-card,#14141c);text-align:center;cursor:pointer;transition:transform .25s cubic-bezier(.2,.8,.2,1),border-color .2s,background-color .2s}
.x-drop__zone:hover{border-color:color-mix(in srgb,var(--d-accent,#4D74FF) 60%,transparent)}
.x-drop__zone.over{transform:scale(1.02);border-color:var(--d-accent,#4D74FF);background:color-mix(in srgb,var(--d-accent,#4D74FF) 10%,var(--d-card,#14141c))}
.x-drop__ic{display:grid;place-items:center;width:30px;height:30px;border-radius:50%;background:color-mix(in srgb,var(--d-accent,#4D74FF) 18%,transparent);color:var(--d-accent,#4D74FF);font:700 15px/1 var(--d-font,system-ui,sans-serif);transition:transform .3s}
.x-drop__zone.over .x-drop__ic{transform:translateY(-3px)}
.x-drop__zone b{font:600 13px/1.3 var(--d-font,system-ui,sans-serif)}
.x-drop__zone small{font:11px/1.3 var(--d-font,system-ui,sans-serif);color:var(--d-muted,#9b9ba8)}
.x-drop__list{display:grid;gap:6px;margin:0;padding:0;list-style:none}
.x-drop__list li{display:grid;grid-template-columns:1fr auto;gap:4px 10px;padding:8px 10px;border-radius:10px;border:1px solid var(--d-line,rgba(255,255,255,.1));background:var(--d-card,#14141c);font:12px/1.2 var(--d-font,system-ui,sans-serif);animation:x-drop-in .3s cubic-bezier(.2,.8,.2,1)}
@keyframes x-drop-in{from{opacity:0;transform:translateY(6px)}}
.x-drop__list li span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
…const init = (root) => {
const zone = root.querySelector('.x-drop__zone'), input = zone.querySelector('input'), list = root.querySelector('.x-drop__list');
const timers = new Set();
const add = (name, size) => {
if (list.children.length >= 3) list.firstElementChild.remove();
const li = document.createElement('li'); li.innerHTML = `<span></span><em>0%</em><div class="x-drop__bar"><i></i></div>`;
li.querySelector('span').textContent = name; list.appendChild(li);
let p = 0; const bar = li.querySelector('i'), em = li.querySelector('em');
const tick = () => { p = Math.min(100, p + 6 + Math.random() * 16); bar.style.transform = `scaleX(${p / 100})`; em.textContent = Math.round(p) + '%';
if (p < 100) { const id = setTimeout(tick, 160); timers.add(id); } else { li.classList.add('ok'); em.textContent = '✓ ' + size; } };
tick();
};
…订阅 Builder 查看完整源码。查看价格