Files
cms-SiteForge/app/static/pro.js
T
浅念 601451f22b upgrade: adopt V8.21.3 (V8.13→8.21.3: P5 SFTP/S3, P6 multilingual, editor overhaul, enterprise hardening)
Overlay ~150 upstream items onto our V8.12 base, preserving trade-pro
theme, registry tests, and design docs. Re-applied our patch set
(discovery/env_for/blocks.css/type-mapping/theme_color/frontend entries,
test_theme_pro_v3 mods) — all anchors survived upstream evolution.

New platform enhancement (fixes upgrade-introduced regression):
multilingual.site_locale now honors settings_json.locale, plus
PUT /api/i18n/sites/{id}/locale — standalone English sites get
<html lang='en'> under the V8.15 locale-rewrite pipeline.

compose: adopt V8.21.3 (image 8.21.3-dev.1, /api/ready probes, MySQL/S3
env) with project name pinned to siteforge-v8-10 (data volumes).

Tests: 356 passed / 11 failed — all 11 confirmed pre-existing in
pristine V8.21.3 on this host (10 upstream Windows env + our build test
now green after locale fix). Patch registry updated in plan doc.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 12:31:03 +08:00

73 lines
7.6 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');
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();
});
})();