app
Component Widgets
Public Made by Adomby adom
Reusable component widgets for pinouts, thermal vias, molecule relationships, reviewed chip outlines and lightweight four-level LOD cache listings.
← Commit history
moleculize: capture dropped .kicad_pcb on the loaded widget too (whole-card drop target) + prevent the browser opening the file in a new tab
2 files changed
+56−12
files/moleculize-widget.js+28−6@@ -195,15 +195,36 @@ const input = root.querySelector('[data-file]'); if (!input) return; input.onchange = () => { if (input.files && input.files[0]) this._loadFile(input.files[0]); };- const drop = root.querySelector('[data-drop]');- if (drop) {- drop.onclick = () => input.click();- drop.ondragover = e => { e.preventDefault(); drop.classList.add('over'); };- drop.ondragleave = () => drop.classList.remove('over');- drop.ondrop = e => { e.preventDefault(); drop.classList.remove('over'); const f = e.dataTransfer && e.dataTransfer.files[0]; if (f) this._loadFile(f); };++ // Accept a dropped .kicad_pcb ANYWHERE on the card — in the empty drop-zone+ // AND once a board is loaded. Without this, a drop on the loaded widget falls+ // through to the browser, which just opens the file in a new tab.+ const card = root.querySelector('.card');+ if (card) {+ const over = e => { e.preventDefault(); e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'; card.classList.add('over'); };+ card.addEventListener('dragenter', over);+ card.addEventListener('dragover', over);+ card.addEventListener('dragleave', e => { if (!card.contains(e.relatedTarget)) card.classList.remove('over'); });+ card.addEventListener('drop', e => {+ e.preventDefault(); e.stopPropagation(); card.classList.remove('over');+ const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];+ if (f) this._loadFile(f);+ }); }+ // Click affordances: the empty drop zone opens the picker; so does the swap.+ const drop = root.querySelector('[data-drop]');+ if (drop) drop.onclick = () => input.click(); const swap = root.querySelector('[data-swap]'); if (swap) swap.onclick = e => { e.stopPropagation(); input.click(); };++ // Belt-and-braces: stop the browser navigating to a file dropped just OUTSIDE+ // the card (the sandboxed readme otherwise opens it in a new tab). Bound once.+ if (typeof window !== 'undefined' && !window.__adomMoleculizeNoOpen) {+ window.__adomMoleculizeNoOpen = true;+ const prevent = e => { e.preventDefault(); };+ window.addEventListener('dragover', prevent, false);+ window.addEventListener('drop', prevent, false);+ } } _css() {@@ -215,6 +236,7 @@ :host([theme="dark"]){--bg:#0d1420;--panel:#121a26;--line:#26324a;--line2:#33425f;--text:#dbe2ec;--dim:#8b97ac;--dim2:#66738c;--accent:#00e6dc;--wing:rgba(0,214,204,.07);--board:#2b3a52;--boardln:#4c9dff;--mp:#ffb454;--grid:#2a3651} *{box-sizing:border-box} .card{background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:16px;color:var(--text);font:14px/1.5 -apple-system,'Segoe UI',Roboto,Arial,sans-serif}+ .card.over{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)} .hd{display:flex;align-items:baseline;gap:10px;margin-bottom:12px} .hd .t{font:700 16px ui-monospace,'SF Mono',Menlo,monospace} .hd .badge{font:700 10px ui-monospace,monospace;letter-spacing:.06em;color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);padding:2px 8px;border-radius:6px}
readme.html+28−6@@ -1021,15 +1021,36 @@ h2 .anchor:hover{opacity:1;color:var(--teal)} const input = root.querySelector('[data-file]'); if (!input) return; input.onchange = () => { if (input.files && input.files[0]) this._loadFile(input.files[0]); };- const drop = root.querySelector('[data-drop]');- if (drop) {- drop.onclick = () => input.click();- drop.ondragover = e => { e.preventDefault(); drop.classList.add('over'); };- drop.ondragleave = () => drop.classList.remove('over');- drop.ondrop = e => { e.preventDefault(); drop.classList.remove('over'); const f = e.dataTransfer && e.dataTransfer.files[0]; if (f) this._loadFile(f); };++ // Accept a dropped .kicad_pcb ANYWHERE on the card — in the empty drop-zone+ // AND once a board is loaded. Without this, a drop on the loaded widget falls+ // through to the browser, which just opens the file in a new tab.+ const card = root.querySelector('.card');+ if (card) {+ const over = e => { e.preventDefault(); e.stopPropagation(); if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'; card.classList.add('over'); };+ card.addEventListener('dragenter', over);+ card.addEventListener('dragover', over);+ card.addEventListener('dragleave', e => { if (!card.contains(e.relatedTarget)) card.classList.remove('over'); });+ card.addEventListener('drop', e => {+ e.preventDefault(); e.stopPropagation(); card.classList.remove('over');+ const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];+ if (f) this._loadFile(f);+ }); }+ // Click affordances: the empty drop zone opens the picker; so does the swap.+ const drop = root.querySelector('[data-drop]');+ if (drop) drop.onclick = () => input.click(); const swap = root.querySelector('[data-swap]'); if (swap) swap.onclick = e => { e.stopPropagation(); input.click(); };++ // Belt-and-braces: stop the browser navigating to a file dropped just OUTSIDE+ // the card (the sandboxed readme otherwise opens it in a new tab). Bound once.+ if (typeof window !== 'undefined' && !window.__adomMoleculizeNoOpen) {+ window.__adomMoleculizeNoOpen = true;+ const prevent = e => { e.preventDefault(); };+ window.addEventListener('dragover', prevent, false);+ window.addEventListener('drop', prevent, false);+ } } _css() {@@ -1041,6 +1062,7 @@ h2 .anchor:hover{opacity:1;color:var(--teal)} :host([theme="dark"]){--bg:#0d1420;--panel:#121a26;--line:#26324a;--line2:#33425f;--text:#dbe2ec;--dim:#8b97ac;--dim2:#66738c;--accent:#00e6dc;--wing:rgba(0,214,204,.07);--board:#2b3a52;--boardln:#4c9dff;--mp:#ffb454;--grid:#2a3651} *{box-sizing:border-box} .card{background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:16px;color:var(--text);font:14px/1.5 -apple-system,'Segoe UI',Roboto,Arial,sans-serif}+ .card.over{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)} .hd{display:flex;align-items:baseline;gap:10px;margin-bottom:12px} .hd .t{font:700 16px ui-monospace,'SF Mono',Menlo,monospace} .hd .badge{font:700 10px ui-monospace,monospace;letter-spacing:.06em;color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);padding:2px 8px;border-radius:6px}