From 1768c6a5c4f20a2fa3ee325ec79fedf40626ff72 Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 25 Jun 2026 08:33:43 +0100 Subject: [PATCH] fix: separate tree arrow (expand) from label (filter) in catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arrow ▶ only toggles expand/collapse. Node label click filters products. Forms inside tree (Add Category, Add District, etc.) no longer trigger navigation when clicking their buttons or inputs. --- src/admin/public/style.css | 7 ++++++- src/admin/views/catalog.js | 25 +++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/admin/public/style.css b/src/admin/public/style.css index 8dee3f1..e6c7d07 100644 --- a/src/admin/public/style.css +++ b/src/admin/public/style.css @@ -291,7 +291,6 @@ pre { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; max-width .tree-node { margin-left: 0; } .tree-toggle { - cursor: pointer; user-select: none; padding: 0.4rem 0; display: flex; @@ -308,10 +307,16 @@ pre { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; max-width transition: transform 0.15s; width: 12px; text-align: center; + cursor: pointer; } .tree-toggle .arrow.open { transform: rotate(90deg); } +.tree-toggle .node-label { + cursor: pointer; + flex: 1; +} + .tree-children { display: none; } .tree-children.open { display: block; } diff --git a/src/admin/views/catalog.js b/src/admin/views/catalog.js index 0005b2c..486599b 100644 --- a/src/admin/views/catalog.js +++ b/src/admin/views/catalog.js @@ -12,7 +12,7 @@ export function renderCatalog(tree, products, filter, categories, subcategories, const editFormHtml = renderProductEditForm('/catalog/products/__ID__/edit', catOptions, subcatJson, locations) .replace(/`/g, '\\`').replace(/\$/g, '\\$'); - let treeHtml = '
All Products(' + products.length + ')
'; + let treeHtml = '
All Products(' + products.length + ')
'; for (const [country, cdata] of Object.entries(tree)) { let countryCount = 0, cityHtml = ''; for (const [city, ddata] of Object.entries(cdata.cities)) { @@ -24,17 +24,17 @@ export function renderCatalog(tree, products, filter, categories, subcategories, districtCount += catCount; let subHtml = ''; for (const s of (c.subs||[])) { - subHtml += `
${esc(s.name)}(${s.pc||0})
`; + subHtml += `
${esc(s.name)}(${s.pc||0})
`; } - catHtml += `
${esc(c.name)}(${catCount})
${subHtml}
`; + catHtml += `
${esc(c.name)}(${catCount})
${subHtml}
`; } - districtHtml += `
${esc(district)}(${districtCount})
${catHtml}
`; + districtHtml += `
${esc(district)}(${districtCount})
${catHtml}
`; cityCount += districtCount; } - cityHtml += `
${esc(city)}(${cityCount})
${districtHtml}
`; + cityHtml += `
${esc(city)}(${cityCount})
${districtHtml}
`; countryCount += cityCount; } - treeHtml += `
${esc(country)}(${countryCount})
${cityHtml}
`; + treeHtml += `
${esc(country)}(${countryCount})
${cityHtml}
`; } let tableHtml = '

No products found.

'; @@ -181,12 +181,17 @@ async function addCategoryInline() { function escHtml(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; } -document.querySelectorAll('.tree-toggle').forEach(el=>{el.addEventListener('click',()=>{ - const ch=el.nextElementSibling; if(!ch||!ch.classList.contains('tree-children')) return; - ch.classList.toggle('open'); el.querySelector('.arrow').classList.toggle('open'); +document.querySelectorAll('.tree-toggle .arrow').forEach(el=>{el.addEventListener('click',e=>{ + e.stopPropagation(); + const toggle=el.closest('.tree-toggle'); + const ch=toggle.nextElementSibling; if(!ch||!ch.classList.contains('tree-children')) return; + ch.classList.toggle('open'); el.classList.toggle('open'); +})}); +document.querySelectorAll('.tree-toggle .node-label').forEach(el=>{el.addEventListener('click',e=>{ + e.stopPropagation(); const loc=el.dataset.loc, cat=el.dataset.cat, sub=el.dataset.sub, all=el.dataset.all; if(loc||cat||sub||all){ let u='/catalog?'; if(loc) u+='loc='+loc; if(cat) u+='cat='+cat; if(sub) u+='sub='+sub; location.href=u; } -})}); +}); function openAdd(){ document.getElementById('modal-body').innerHTML=addFormTpl; initLocationSelects(null); document.getElementById('product-modal').style.display='flex'; } async function openEdit(id){ const r=await fetch('/catalog/products/'+id+'/json'); const p=await r.json(); document.getElementById('modal-body').innerHTML=editFormTpl.replace('/__ID__/','/'+p.id+'/'); fillEditForm(p); document.getElementById('product-modal').style.display='flex'; }