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