feat: add 41 categories, interactive tooltip, IB links to vendor page, vendor info bar
This commit is contained in:
@@ -687,6 +687,7 @@
|
||||
.vendor-tooltip.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
pointer-events: auto;
|
||||
}
|
||||
.vtt-logo {
|
||||
width: 100%;
|
||||
@@ -754,3 +755,46 @@
|
||||
border: 1px solid #d0e0ff;
|
||||
}
|
||||
.vtt-link:hover { opacity: .85; }
|
||||
/* Vendor info bar in results */
|
||||
.vendor-info-bar {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 18px;
|
||||
background: linear-gradient(135deg, #f0f5ff 0%, #e8f0ff 100%);
|
||||
border: 1px solid #c8d8f5;
|
||||
border-radius: 16px;
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.vendor-info-bar + .vendor-info-bar { margin-top: -8px; }
|
||||
.vib-logo {
|
||||
flex-shrink: 0;
|
||||
width: 100px;
|
||||
height: 64px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #dae6ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
.vib-logo img { max-width: 100%; max-height: 48px; object-fit: contain; }
|
||||
.vib-logo-text { font-size: 18px; font-weight: 800; color: #3978e0; }
|
||||
.vib-info { flex: 1; min-width: 0; }
|
||||
.vib-name { font-size: 15px; font-weight: 800; color: #1a3e79; margin: 0 0 5px; }
|
||||
.vib-desc { font-size: 13px; color: #4a5d7a; line-height: 1.55; margin: 0 0 10px; }
|
||||
.vib-links { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
.vib-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
transition: .15s ease;
|
||||
}
|
||||
.vib-link.mont { background: linear-gradient(135deg, #1f4ea3, #3978e0); color: #fff; }
|
||||
.vib-link.site { background: #fff; color: #2a5aaa; border: 1px solid #c8d8f5; }
|
||||
.vib-link:hover { opacity: .82; transform: translateY(-1px); }
|
||||
|
||||
+48
-8
@@ -30,8 +30,12 @@
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'vendor-tooltip';
|
||||
document.body.appendChild(tooltip);
|
||||
tooltip.addEventListener('mouseenter', () => { overTooltip = true; clearTimeout(hideTimer); });
|
||||
tooltip.addEventListener('mouseleave', () => { overTooltip = false; hideTooltip(); });
|
||||
|
||||
let tooltipTimer = null;
|
||||
let hideTimer = null;
|
||||
let overTooltip = false;
|
||||
let vendorMap = {};
|
||||
|
||||
function buildVendorMap() {
|
||||
@@ -82,8 +86,12 @@
|
||||
tooltip.classList.add('visible');
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
tooltip.classList.remove('visible');
|
||||
function hideTooltip(immediate) {
|
||||
clearTimeout(hideTimer);
|
||||
if (immediate) { tooltip.classList.remove('visible'); return; }
|
||||
hideTimer = setTimeout(() => {
|
||||
if (!overTooltip) tooltip.classList.remove('visible');
|
||||
}, 120);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,6 +252,8 @@
|
||||
function renderResults() {
|
||||
const { allowedCategories, allowedVendors, allowedProducts, productsByVendor, categoriesByProduct } = visibleSets();
|
||||
const productsById = new Map(state.data.products.map(p => [p.id, p]));
|
||||
const vendorsById = new Map(state.data.vendors.map(v => [v.id, v]));
|
||||
const isIb = state.scope === 'ib';
|
||||
|
||||
const rows = [];
|
||||
for (const vendor of state.data.vendors) {
|
||||
@@ -257,7 +267,7 @@
|
||||
});
|
||||
const products = productIds.map(pId => productsById.get(pId)).filter(Boolean);
|
||||
if (products.length === 0) continue;
|
||||
rows.push({ vendor: vendor.name, products });
|
||||
rows.push({ vendor, products });
|
||||
}
|
||||
|
||||
el.resultRows.innerHTML = "";
|
||||
@@ -266,21 +276,51 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Vendor info bars for selected vendors
|
||||
if (state.selectedVendors.size > 0) {
|
||||
for (const vid of state.selectedVendors) {
|
||||
const v = vendorsById.get(vid);
|
||||
if (!v) continue;
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'vendor-info-bar';
|
||||
const logoInner = v.logo
|
||||
? `<img src="/static/${v.logo}" alt="${v.name}" onerror="this.parentElement.innerHTML='<span class=vib-logo-text>${v.name.slice(0,2).toUpperCase()}</span>'">`
|
||||
: `<span class="vib-logo-text">${v.name.slice(0,2).toUpperCase()}</span>`;
|
||||
let linksHtml = '';
|
||||
if (v.mont_page) linksHtml += `<a class="vib-link mont" href="${v.mont_page}" target="_blank" rel="noopener">MONT ↗</a>`;
|
||||
if (v.website) linksHtml += `<a class="vib-link site" href="${v.website}" target="_blank" rel="noopener">Сайт ↗</a>`;
|
||||
bar.innerHTML = `
|
||||
<div class="vib-logo">${logoInner}</div>
|
||||
<div class="vib-info">
|
||||
<div class="vib-name">${v.name}</div>
|
||||
${v.description ? `<div class="vib-desc">${v.description}</div>` : ''}
|
||||
${linksHtml ? `<div class="vib-links">${linksHtml}</div>` : ''}
|
||||
</div>`;
|
||||
el.resultRows.appendChild(bar);
|
||||
}
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
const card = document.createElement("article");
|
||||
card.className = "row-card";
|
||||
const title = document.createElement("strong");
|
||||
title.textContent = row.vendor;
|
||||
title.textContent = row.vendor.name;
|
||||
card.appendChild(title);
|
||||
const tags = document.createElement("div");
|
||||
tags.className = "tags";
|
||||
for (const product of row.products) {
|
||||
const hasUrl = product.url && String(product.url).trim().length > 0;
|
||||
const tag = document.createElement(hasUrl ? "a" : "span");
|
||||
// IB scope: link to vendor's mont page; infra: link to product url
|
||||
let url = '';
|
||||
if (isIb) {
|
||||
url = row.vendor.mont_page || '';
|
||||
} else {
|
||||
url = (product.url && String(product.url).trim()) || '';
|
||||
}
|
||||
const tag = document.createElement(url ? "a" : "span");
|
||||
tag.className = "tag";
|
||||
tag.textContent = product.name;
|
||||
if (hasUrl) {
|
||||
tag.href = product.url;
|
||||
if (url) {
|
||||
tag.href = url;
|
||||
tag.target = "_blank";
|
||||
tag.rel = "noopener noreferrer";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user