Free shipping on all orders

All Products

Browse our complete catalog

No results

No products match your filters

Try removing some filters or browsing all products

Clear All Filters
// ── 全局 Header 分类菜单(mega menu + 移动端手风琴) ── // 服务端 HTML 渲染骨架(
+ data-* 容器),客户端 JS 拉 collections.json // 后填充分组数据。与首页 src/components/layout/Header.tsx 视觉一致。 (function(){ // 分组规则(与 Header.tsx CATEGORY_GROUPS 同步) var CATEGORY_GROUPS = [ { name: 'Tires & Wheels', match: ['car-tires','atv-tires','suv-tires','truck-tires','motorcycle-tires','trailer-tires','lawn-mower-tires','golf-cart-tires','motor-home-and-rv-tires','farm-and-tractor-tires','wheels-and-rims'] }, { name: 'Outdoor Cooking', match: ['gas-grills','charcoal-grills','flat-top-grills','combo-grills','smokers','outdoor-fryers'] }, { name: 'Outdoor Living', match: ['above-ground-pools','inflatable-pools','hot-tubs','patio-dining-sets','patio-conversation-sets','patio-chairs','outdoor-sofas-sectionals','outdoor-heating','beach-chairs'] }, { name: 'Tools & Equipment', match: ['power-tools','hand-tools','hand-tool-sets','drills-drivers','power-saws','air-tools-compressors','measuring-layout','fasteners','pro-tools','toolstorage','replacement-parts-accessories','protective-equipment'] }, { name: 'Outdoor Power', match: ['chainsaws','pole-saws','leaf-blowers','string-trimmers','trillers-cultivators','pressure-washers','logsplitters','push-mowers','riding-lawn-mowers','generators'] }, { name: 'Lighting', match: ['desk-lamps','floor-lamps','table-lamps','lamps','lamp-sets','ceiling-lights-fixtures','ceiling-fans','novelty-lights','led-lighting'] }, { name: 'Home Improvement', match: ['plumbing','bathroom-renovation','kitchen-renovation','building-materials','wallpaper','wallpaper-wall-decals-wall-coverings','grey-vinyl-flooring-vinyl-plank-flooring','paint-tools-supplies-paint-brushes-rollers-trays','adhesives-glues-in-paint-supplies-tools','damage-free-hanging','moving','garage-storage-supplies','ladders-and-step-stools','toilet-seats-and-lids','shower-heads','water-filtration','sheds','mattresses-accessories'] }, { name: 'Climate Control', match: ['air-conditioners','air-purifiers','dehumidifiers','humidifiers','fans-in-cooling','portable-and-electric-space-heaters'] }, { name: 'Bikes & Rides', match: ['electric-bikes','mountain-bikes','road-bikes','cruiser-bikes','kids-bikes','scooters'] }, { name: 'Fitness & More', match: ['home-gyms','home-gym-accessories','factory-reconditioned','woodworking'] } ]; function groupCollections(collections) { // 跟踪每个 collection 是否被某个预定义分组匹配过 var matched = {}; var groups = CATEGORY_GROUPS.map(function(g) { return { name: g.name, links: collections.filter(function(c) { if (g.match.indexOf(c.slug) !== -1) { matched[c.slug] = true; return true; } return false; }).map(function(c) { return { href: '/collections/' + c.slug, label: c.title || c.slug }; }) }; }).filter(function(g) { return g.links.length > 0; }); // 兜底分组:把不在任何预定义分组里的分类归到 "More Categories" // 场景:deploy task 上传的 sample 数据只有 1 个 filing-products 分类, // 不在 CATEGORY_GROUPS 任一分组里,会导致 mega menu 整个空。 // 加兜底分组让 sample 数据也能显示分类入口,跑 upload task 后真实数据 // 会按预定义分组归类,未匹配的(如 filing-products)依然走兜底分组。 var unmatched = collections.filter(function(c) { return !matched[c.slug]; }).map(function(c) { return { href: '/collections/' + c.slug, label: c.title || c.slug }; }); if (unmatched.length > 0) { groups.push({ name: 'More Categories', links: unmatched }); } return groups; } function renderMegaMenu(groups) { var groupsContainer = document.querySelector('[data-groups-container]'); var groupTitle = document.querySelector('[data-group-title]'); var groupCount = document.querySelector('[data-group-count]'); var groupLinks = document.querySelector('[data-group-links]'); if (!groupsContainer || !groupLinks) return; var activeIdx = 0; groupsContainer.innerHTML = groups.map(function(g, i) { return ''; }).join(''); function showGroup(idx) { var g = groups[idx]; if (!g) return; activeIdx = idx; if (groupTitle) groupTitle.textContent = g.name; if (groupCount) groupCount.textContent = g.links.length + ' collections'; groupLinks.innerHTML = g.links.map(function(l) { return '' + '' + l.label + ''; }).join(''); // 高亮当前分组 var btns = groupsContainer.querySelectorAll('[data-group-idx]'); btns.forEach(function(btn, i) { var isActive = i === idx; btn.className = 'relative flex w-full items-center justify-between px-5 py-2.5 text-left text-sm transition-all duration-200 ' + (isActive ? 'bg-white font-semibold text-navy' : 'text-gray-600 hover:bg-white/60 hover:text-navy'); var cnt = btn.querySelector('span:last-child'); if (cnt) cnt.className = 'text-[10px] font-medium tracking-wide ' + (isActive ? 'text-navy' : 'text-gray-400'); }); } // 分组按钮点击 groupsContainer.addEventListener('click', function(e) { var btn = e.target.closest('[data-group-idx]'); if (!btn) return; showGroup(parseInt(btn.getAttribute('data-group-idx'), 10)); }); groupsContainer.addEventListener('mouseover', function(e) { var btn = e.target.closest('[data-group-idx]'); if (!btn) return; showGroup(parseInt(btn.getAttribute('data-group-idx'), 10)); }); showGroup(0); } function renderMobileAccordion(groups) { var container = document.getElementById('header-mobile-categories'); if (!container) return; container.innerHTML = groups.map(function(g) { return '
' + '' + '' + g.name + '' + '' + '' + '
' + g.links.map(function(l) { return '' + '' + l.label + ''; }).join('') + '
' + '
'; }).join(''); } // PC mega menu 用 details 的 open 属性控制显示(替代 hidden class) // 原因:data-mega-panel 在 details 里,浏览器 UA 样式 details:not([open])>*:not(summary){display:none} // 会强制隐藏未 open 的 details 子元素,单纯 toggle hidden class 无效 // 方案:mouseenter 自动 open=true,mouseleave 自动 open=false; // 点击 summary 走 details 默认行为(toggle open),不再 preventDefault var megaDetails = document.getElementById('header-mega-menu'); if (megaDetails) { megaDetails.addEventListener('mouseenter', function() { megaDetails.open = true; }); megaDetails.addEventListener('mouseleave', function() { megaDetails.open = false; }); } // 移动端菜单 toggle(与首页 Header.tsx 一致:全宽 slide-down + 图标切换 + max-h/opacity 过渡) var mobileToggle = document.getElementById('mobile-menu-toggle'); var mobilePanel = document.getElementById('mobile-menu-panel'); var iconBars = document.getElementById('mobile-menu-icon-bars'); var iconX = document.getElementById('mobile-menu-icon-x'); function setMobileOpen(open) { if (!mobileToggle || !mobilePanel) return; mobileToggle.setAttribute('aria-expanded', String(open)); mobileToggle.setAttribute('aria-label', open ? 'Close menu' : 'Open menu'); if (open) { mobilePanel.classList.remove('max-h-0', 'opacity-0'); mobilePanel.classList.add('max-h-[85vh]', 'opacity-100'); if (iconBars && iconX) { iconBars.classList.add('hidden'); iconX.classList.remove('hidden'); } } else { mobilePanel.classList.remove('max-h-[85vh]', 'opacity-100'); mobilePanel.classList.add('max-h-0', 'opacity-0'); if (iconBars && iconX) { iconBars.classList.remove('hidden'); iconX.classList.add('hidden'); } } } if (mobileToggle) { mobileToggle.addEventListener('click', function() { var isOpen = mobileToggle.getAttribute('aria-expanded') === 'true'; setMobileOpen(!isOpen); }); } // 点击菜单内链接后自动关闭(与首页 onClick={() => setMobileOpen(false)} 行为一致) document.querySelectorAll('[data-mobile-close]').forEach(function(a) { a.addEventListener('click', function() { setMobileOpen(false); }); }); // 路由变化(hashchange / popstate)时关闭菜单,避免返回上一页后菜单仍展开 window.addEventListener('hashchange', function() { setMobileOpen(false); }); window.addEventListener('popstate', function() { setMobileOpen(false); }); // 拉 collections.json 填充菜单(带 sessionStorage 缓存,5 分钟内不重复请求) var CACHE_KEY = 'lnr_collections_cache'; var CACHE_TTL = 5 * 60 * 1000; function loadCollections() { try { var cached = JSON.parse(sessionStorage.getItem(CACHE_KEY) || 'null'); if (cached && cached.ts && Date.now() - cached.ts < CACHE_TTL && Array.isArray(cached.data)) { return Promise.resolve(cached.data); } } catch(e) {} var host = window.location.hostname.replace(/^www./, ''); var r2Base = 'https://cdn.imagecdn24.mom'; return fetch(r2Base + '/stores/' + host + '/collections.json', { cache: 'no-store' }) .then(function(r) { return r.ok ? r.json() : []; }) .then(function(cols) { if (!Array.isArray(cols)) return []; try { sessionStorage.setItem(CACHE_KEY, JSON.stringify({ ts: Date.now(), data: cols })); } catch(e) {} return cols.filter(function(c) { return c && typeof c.slug === 'string'; }); }) .catch(function() { return []; }); } function init() { loadCollections().then(function(collections) { if (!collections || collections.length === 0) return; var groups = groupCollections(collections); if (groups.length === 0) return; renderMegaMenu(groups); renderMobileAccordion(groups); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();