@@ -78,41 +115,84 @@
(function () {
const zone = document.getElementById('dropZone');
const input = document.getElementById('dropInput');
- const hint = document.getElementById('dropHint');
- const form = document.getElementById('publishForm');
+ const filename = document.getElementById('dropFilename');
+ const sizeEl = document.getElementById('dropSize');
+ const errorEl = document.getElementById('dropError');
+ const clearBtn = document.getElementById('dropClear');
if (!zone || !input) return;
- const ACTIVE = ['border-indigo-500', 'bg-indigo-500/10'];
+ const panels = {
+ empty: zone.querySelector('[data-state="empty"]'),
+ ready: zone.querySelector('[data-state="ready"]'),
+ error: zone.querySelector('[data-state="error"]'),
+ };
+
+ const READY = ['border-emerald-500', '!border-solid', 'bg-emerald-500/5'];
+ const OVER = ['border-indigo-500', 'bg-indigo-500/10', 'scale-[1.01]'];
+ const ERROR = ['border-red-500', 'bg-red-500/5'];
+
+ function setState(name) {
+ for (const key of Object.keys(panels)) {
+ const show = key === name;
+ panels[key].classList.toggle('hidden', !show);
+ panels[key].classList.toggle('flex', show);
+ }
+ zone.classList.remove(...READY, ...OVER, ...ERROR);
+ if (name === 'ready') zone.classList.add(...READY);
+ else if (name === 'error') zone.classList.add(...ERROR);
+ }
+
const prevent = e => { e.preventDefault(); e.stopPropagation(); };
['dragenter', 'dragover'].forEach(ev =>
- zone.addEventListener(ev, e => { prevent(e); zone.classList.add(...ACTIVE); }));
+ zone.addEventListener(ev, e => { prevent(e); zone.classList.add(...OVER); }));
['dragleave', 'drop'].forEach(ev =>
- zone.addEventListener(ev, e => { prevent(e); zone.classList.remove(...ACTIVE); }));
+ zone.addEventListener(ev, e => { prevent(e); zone.classList.remove(...OVER); }));
zone.addEventListener('drop', e => {
const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
if (!f) return;
- if (!/\.html?$/i.test(f.name) && f.type !== 'text/html') {
- hint.textContent = 'Only .html files are accepted';
- hint.classList.add('text-red-400');
- return;
- }
- // DataTransfer into the input so form submit picks it up.
- const dt = new DataTransfer();
- dt.items.add(f);
- input.files = dt.files;
- showSelection(f);
+ handleFile(f);
});
input.addEventListener('change', () => {
- if (input.files && input.files[0]) showSelection(input.files[0]);
+ const f = input.files && input.files[0];
+ if (f) handleFile(f);
});
- function showSelection(f) {
- const kb = (f.size / 1024).toFixed(1);
- hint.textContent = `${f.name} · ${kb} KB`;
- hint.classList.remove('text-red-400');
- hint.classList.add('text-indigo-300');
+ clearBtn && clearBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ input.value = '';
+ setState('empty');
+ });
+
+ function handleFile(f) {
+ if (!/\.html?$/i.test(f.name) && f.type !== 'text/html') {
+ errorEl.textContent = `Not an HTML file: ${f.name}`;
+ setState('error');
+ setTimeout(() => { if (!input.files[0]) setState('empty'); }, 2500);
+ return;
+ }
+ if (f.size > 5 * 1024 * 1024) {
+ errorEl.textContent = `Too large (${fmtSize(f.size)}, max 5 MB)`;
+ setState('error');
+ return;
+ }
+ // Reflect drop selection back into the input so the form submits it.
+ try {
+ const dt = new DataTransfer();
+ dt.items.add(f);
+ input.files = dt.files;
+ } catch (_) { /* no-op: input.files already set if via click */ }
+ filename.textContent = f.name;
+ sizeEl.textContent = fmtSize(f.size);
+ setState('ready');
+ }
+
+ function fmtSize(n) {
+ if (n < 1024) return `${n} B`;
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
}
})();