Attach some files
Drop them, paste a screenshot, or pick them. The ones that break a rule stay in the list and say which one.
<div class="demo demo--wide">
<form id="form">
<label class="demo-label" for="docs">Attachments</label>
<input id="docs" name="docs[]" type="file">
</form>
<div class="demo-row">
<button class="demo-btn demo-btn--ghost" id="good">Add a photo</button>
<button class="demo-btn demo-btn--ghost" id="big">Add one that is too big</button>
<button class="demo-btn demo-btn--ghost" id="wrong">Add the wrong kind</button>
</div>
<p class="demo-hint">Nothing leaves your browser: there is no server here, so the files stay in the
real file input — which is exactly what happens when you leave <code>url</code> out.</p>
<p class="demo-out" id="out"></p>
</div>
import { createFileUploader } from 'https://www.devix.pk/cdn/oss/file-uploader@1.0.0/index.js';
const out = document.getElementById('out');
const uploader = createFileUploader(document.getElementById('docs'), {
accept: 'image/*,.pdf',
maxSize: '2MB',
maxFiles: 6,
onAdd: () => report(),
onReject: () => report(),
onRemove: () => report(),
});
function report() {
// With no `url`, the real input holds the files — so this is what the form would post.
// An empty file input still contributes one nameless entry, so drop that.
const posted = [...new FormData(document.getElementById('form')).getAll('docs[]')].map((f) => f.name).filter(Boolean);
out.textContent = posted.length ? `The form would post: ${posted.join(', ')}` : 'Nothing to post yet.';
out.dataset.state = posted.length ? 'ok' : '';
}
// Files made in the browser, so the demo needs nothing from your disk.
const make = (name, bytes, type) => new File([new Uint8Array(bytes)], name, { type });
/** A real picture, so the preview is a real preview. */
async function photo() {
const canvas = Object.assign(document.createElement('canvas'), { width: 320, height: 200 });
const context = canvas.getContext('2d');
const hue = Math.floor(Math.random() * 360);
const sky = context.createLinearGradient(0, 0, 0, 200);
sky.addColorStop(0, `hsl(${hue} 70% 62%)`);
sky.addColorStop(1, `hsl(${(hue + 40) % 360} 70% 38%)`);
context.fillStyle = sky;
context.fillRect(0, 0, 320, 200);
context.fillStyle = `hsl(${(hue + 180) % 360} 60% 25%)`;
context.beginPath();
context.moveTo(0, 200);
context.lineTo(120, 96);
context.lineTo(210, 200);
context.closePath();
context.fill();
const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
return new File([blob], `photo-${Date.now() % 1000}.png`, { type: 'image/png' });
}
document.getElementById('good').addEventListener('click', async () => uploader.addFiles([await photo()]));
document.getElementById('big').addEventListener('click', () => uploader.addFiles([make('holiday.png', 9_400_000, 'image/png')]));
document.getElementById('wrong').addEventListener('click', () => uploader.addFiles([make('archive.zip', 30_000, 'application/zip')]));
report();
@import url('https://www.devix.pk/cdn/oss/file-uploader@1.0.0/file-uploader.min.css');
.demo-label { display: block; margin-bottom: 8px; }
#out[data-state='ok'] { font-weight: 600; }