Files
cms-SiteForge/app/static/pro.js
T
浅念 8884e100f9 upgrade: adopt V8.31.1 (theme 1.3/1.4 redesigns, editor deep-canvas, guided launch, component library)
Overlay 190 upstream items (V8.22→8.31) preserving trade-pro theme,
registry tests, and design docs. Re-applied full patch set on identical
anchors (discovery/env_for/blocks.css/type-mapping/locale-override/
theme_color/frontend entries/test_theme_pro_v3 mods) plus compose pins.

Verified: theme suites 35/35; dual-baseline full run 415 passed vs
pristine 386+29 ours, FAILED-set diff EMPTY (same 10 Windows env fails).
Live: container 8.31.1-dev.1 healthy, hesupipe 192 entries intact,
trade-pro build 203 pages 0 errors.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-26 09:43:17 +08:00

77 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';(()=>{
const menu=document.querySelector('[data-pro-menu]'); const nav=document.querySelector('#pro-navigation');
if(menu&&nav){menu.addEventListener('click',()=>{const open=menu.getAttribute('aria-expanded')!=='true';menu.setAttribute('aria-expanded',String(open));nav.classList.toggle('is-open',open);menu.setAttribute('aria-label',open?'关闭导航':'打开导航');});}
// Drawer navigation. The same markup is used across the four professional themes.
const mobileNav=document.querySelector('#pro-navigation');
let drawerBackdrop=null,drawerClose=null,returnFocus=null;
if(menu&&mobileNav){
const originalParent=mobileNav.parentNode,originalNext=mobileNav.nextSibling;
const heading=document.createElement('div');heading.className='sf-drawer-header';
const title=document.createElement('strong');title.textContent='网站导航';
drawerClose=document.createElement('button');drawerClose.type='button';drawerClose.className='sf-drawer-close';drawerClose.textContent='×';drawerClose.setAttribute('aria-label','关闭网站导航');
heading.append(title,drawerClose);mobileNav.prepend(heading);
drawerBackdrop=document.createElement('div');drawerBackdrop.className='sf-mobile-drawer-backdrop';drawerBackdrop.hidden=false;document.body.append(drawerBackdrop);
function setDrawer(open,focusBack=true){
const isMobile=window.matchMedia('(max-width:1024px)').matches;
open=Boolean(open&&isMobile);
if(open){returnFocus=document.activeElement;document.body.append(mobileNav);}
else if(mobileNav.parentNode!==originalParent){if(originalNext&&originalNext.parentNode===originalParent)originalParent.insertBefore(mobileNav,originalNext);else originalParent.append(mobileNav);}
mobileNav.classList.toggle('is-open',open);drawerBackdrop.classList.toggle('is-open',open);
document.body.classList.toggle('sf-drawer-open',open);
menu.setAttribute('aria-expanded',String(open));menu.setAttribute('aria-label',open?'关闭导航':'打开导航');
mobileNav.setAttribute('aria-hidden',String(!open&&isMobile));
if('inert' in mobileNav)mobileNav.inert=!open&&isMobile;
if(open)drawerClose.focus();
else if(focusBack&&returnFocus instanceof HTMLElement){returnFocus.focus();returnFocus=null;}
}
// Replace the old inline-expansion listener: use capture to stop the old listener.
menu.addEventListener('click',e=>{if(!window.matchMedia('(max-width:1024px)').matches)return;e.stopImmediatePropagation();setDrawer(!mobileNav.classList.contains('is-open'));},true);
drawerClose.addEventListener('click',()=>setDrawer(false));
drawerBackdrop.addEventListener('click',()=>setDrawer(false));
document.querySelectorAll('[data-mobile-open-menu]').forEach(b=>b.addEventListener('click',()=>setDrawer(true)));
mobileNav.addEventListener('click',e=>{if(e.target.closest('a'))setDrawer(false,false)});
document.addEventListener('keydown',e=>{
if(e.key==='Escape'&&mobileNav.classList.contains('is-open')){e.preventDefault();setDrawer(false);}
if(e.key!=='Tab'||!mobileNav.classList.contains('is-open'))return;
const els=[...mobileNav.querySelectorAll('button:not([disabled]),a[href]')].filter(el=>el.getClientRects().length&&!el.closest('[hidden]'));
if(!els.length)return;const first=els[0],last=els[els.length-1];
if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus();}
else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus();}
});
window.matchMedia('(max-width:1024px)').addEventListener('change',()=>setDrawer(false,false));
if('inert' in mobileNav&&window.matchMedia('(max-width:1024px)').matches)mobileNav.inert=true;
// Do not strand catalogue links when the old mobile mega-button is hidden.
const megaLinks=document.querySelectorAll('[data-pro-mega] a');
if(megaLinks.length){const wrap=document.createElement('div');wrap.className='sf-drawer-extra';const h=document.createElement('strong');h.textContent='更多分类与系列';wrap.append(h);
[...megaLinks].slice(0,20).forEach(a=>{const clone=a.cloneNode(true);wrap.append(clone);});mobileNav.append(wrap);}
}
const tabbar=document.querySelector('[data-mobile-tabs]');
if(tabbar){tabbar.style.setProperty('--sf-mobile-tab-count',String(tabbar.children.length));}
const megaButton=document.querySelector('[data-pro-catalog]'), mega=document.querySelector('[data-pro-mega]');
if(megaButton&&mega){function setMega(open){mega.hidden=!open;mega.classList.toggle('is-open',open);megaButton.setAttribute('aria-expanded',String(open));}
megaButton.addEventListener('click',()=>setMega(mega.hidden));
document.addEventListener('keydown',e=>{if(e.key==='Escape')setMega(false);});
document.addEventListener('click',e=>{if(!mega.hidden&&!mega.contains(e.target)&&!megaButton.contains(e.target))setMega(false);});
}
document.querySelectorAll('.pro-nav-disclose').forEach(button=>{button.addEventListener('click',()=>{let panel=button.parentElement.querySelector('.pro-nav-children');let visible=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!visible));panel.hidden=visible;});});
document.querySelectorAll('.pro-gallery-thumbs').forEach(container=>{const main=container.parentElement.querySelector('.pro-product-media img');const buttons=[...container.querySelectorAll('[data-gallery-image]')];
buttons.forEach(b=>{b.addEventListener('click',()=>{buttons.forEach(x=>x.setAttribute('aria-pressed',String(x===b)));if(main){main.src=b.dataset.galleryImage;main.alt=b.querySelector('img')?.alt||'';}});});
if(!main)return;main.tabIndex=0;main.style.cursor='zoom-in';const open=()=>{const pane=document.createElement('div');pane.className='pro-gallery-lightbox';pane.setAttribute('role','dialog');pane.setAttribute('aria-label','产品大图预览');let img=document.createElement('img');img.src=main.src;img.alt=main.alt;const close=document.createElement('button');close.type='button';close.textContent='关闭 ×';pane.append(img,close);document.body.append(pane);const done=()=>{pane.remove();document.removeEventListener('keydown',esc);main.focus();};const esc=e=>{if(e.key==='Escape')done();};document.addEventListener('keydown',esc);close.addEventListener('click',done);pane.addEventListener('click',e=>{if(e.target===pane)done();});close.focus();};main.addEventListener('click',open);main.addEventListener('keydown',e=>{if(e.key==='Enter')open();});
});
document.querySelectorAll('.pro-filter-scope').forEach(scope=>{
const panel=scope.previousElementSibling;if(!panel||!panel.classList.contains('pro-filter-panel'))return;
const cards=Array.from(scope.querySelectorAll('.pro-card'));
const blank=document.createElement('div');blank.className='pro-filter-empty';blank.hidden=true;blank.textContent='没有找到符合条件的内容,请调整关键词或筛选条件。';scope.append(blank);
const q=panel.querySelector('[data-pro-filter-search]'),b=panel.querySelector('[data-pro-filter-brand]'),c=panel.querySelector('[data-pro-filter-category]'),count=panel.querySelector('[data-pro-filter-count]');
function options(sel,key){const vals=[...new Set(cards.map(x=>x.dataset[key]||'').filter(Boolean))].sort();vals.forEach(x=>sel.add(new Option(x,x)));if(!vals.length)sel.closest('label').hidden=true;}
options(b,'brand');options(c,'category');
// Support shareable searches from automotive homepage without changing page URLs.
const params=new URLSearchParams(window.location.search);
if(q && params.has('q')) q.value=(params.get('q')||'').slice(0,120);
if(b && params.has('brand') && [...b.options].some(o=>o.value===params.get('brand'))) b.value=params.get('brand');
function run(){const str=q.value.trim().toLocaleLowerCase();let visible=0;
cards.forEach(el=>{const hit=el.textContent.toLocaleLowerCase().includes(str)&&(!b.value||el.dataset.brand===b.value)&&(!c.value||el.dataset.category===c.value);el.hidden=!hit;visible+=Number(hit);});count.textContent=`共 ${visible} 条`;blank.hidden=visible!==0;}
[q,b,c].forEach(x=>x.addEventListener(x===q?'input':'change',run));run();
});
})();