UI: стиль главной страницы, новый лого и favicon
- Заменён логотип на новый (MONT Group of companies) - Favicon взят с stend.4mont.ru, перенесён в static/ - Лого фиксировано в левом верхнем углу (position:fixed), уменьшено на 30% - Добавлена кнопка «Инфраструктурный полигон МОНТ» → stend.4mont.ru - Градиентный текст h1, анимация fadeUp при загрузке - Акцент-полоски в заголовках карточек, hover-эффекты на row-card - Статистика переведена на таблетки (stat-pill) - Счётчики-бейджи рядом с «Вендоры» и «Категории» - Кастомный скроллбар в панелях чипов - Улучшены теги продуктов, кнопка сброса фильтров
This commit is contained in:
+91
-3
@@ -17,6 +17,11 @@
|
||||
clearBtn: document.getElementById("clearBtn"),
|
||||
modeInfra: document.getElementById("modeInfra"),
|
||||
modeIb: document.getElementById("modeIb"),
|
||||
resultSection: document.querySelector(".result"),
|
||||
filtersSection: document.querySelector(".board"),
|
||||
activeFilters: document.getElementById("activeFilters"),
|
||||
vendorBadge: document.getElementById("vendorBadge"),
|
||||
categoryBadge: document.getElementById("categoryBadge"),
|
||||
};
|
||||
|
||||
let clickAudioCtx = null;
|
||||
@@ -130,9 +135,12 @@
|
||||
else if (!allowedVendors.has(vendor.id)) node.classList.add("dim");
|
||||
node.textContent = vendor.name;
|
||||
node.addEventListener("click", () => {
|
||||
if (state.selectedVendors.has(vendor.id)) state.selectedVendors.delete(vendor.id);
|
||||
const wasSelected = state.selectedVendors.has(vendor.id);
|
||||
if (wasSelected) state.selectedVendors.delete(vendor.id);
|
||||
else state.selectedVendors.add(vendor.id);
|
||||
render();
|
||||
if (wasSelected) scrollAfterDeselect();
|
||||
else scrollToResultsSmooth();
|
||||
});
|
||||
el.vendorList.appendChild(node);
|
||||
}
|
||||
@@ -148,14 +156,20 @@
|
||||
else if (!allowedCategories.has(category.id)) node.classList.add("dim");
|
||||
node.textContent = category.name;
|
||||
node.addEventListener("click", () => {
|
||||
if (state.selectedCategories.has(category.id)) state.selectedCategories.delete(category.id);
|
||||
const wasSelected = state.selectedCategories.has(category.id);
|
||||
if (wasSelected) state.selectedCategories.delete(category.id);
|
||||
else state.selectedCategories.add(category.id);
|
||||
render();
|
||||
if (wasSelected) scrollAfterDeselect();
|
||||
else scrollToResultsSmooth();
|
||||
});
|
||||
el.categoryList.appendChild(node);
|
||||
}
|
||||
|
||||
el.stats.textContent = `Вендоров: ${allowedVendors.size}/${state.data.vendors.length} | Категорий: ${allowedCategories.size}/${state.data.categories.length} | Продуктов: ${visibleSets().allowedProducts.size}/${state.data.products.length}`;
|
||||
const ps = visibleSets().allowedProducts.size;
|
||||
el.stats.innerHTML = `<span class="stat-pill">Вендоры <b>${allowedVendors.size}</b> / ${state.data.vendors.length}</span><span class="stat-pill">Категории <b>${allowedCategories.size}</b> / ${state.data.categories.length}</span><span class="stat-pill">Продукты <b>${ps}</b> / ${state.data.products.length}</span>`;
|
||||
if (el.vendorBadge) el.vendorBadge.textContent = allowedVendors.size;
|
||||
if (el.categoryBadge) el.categoryBadge.textContent = allowedCategories.size;
|
||||
}
|
||||
|
||||
function renderResults() {
|
||||
@@ -213,9 +227,82 @@
|
||||
el.modeIb.classList.toggle("active", state.scope === "ib");
|
||||
document.body.classList.toggle("scope-ib", state.scope === "ib");
|
||||
renderChips();
|
||||
renderActiveFilters();
|
||||
renderResults();
|
||||
}
|
||||
|
||||
function scrollToResultsSmooth() {
|
||||
if (!el.resultSection) return;
|
||||
const top = Math.max(el.resultSection.getBoundingClientRect().top + window.scrollY - 10, 0);
|
||||
window.scrollTo({ top, behavior: "smooth" });
|
||||
}
|
||||
|
||||
function scrollToFiltersSmooth() {
|
||||
if (!el.filtersSection) return;
|
||||
const top = Math.max(el.filtersSection.getBoundingClientRect().top + window.scrollY - 8, 0);
|
||||
window.scrollTo({ top, behavior: "smooth" });
|
||||
}
|
||||
|
||||
function scrollToTopSmooth() {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
function scrollAfterDeselect() {
|
||||
if (window.innerWidth > 980) {
|
||||
scrollToTopSmooth();
|
||||
return;
|
||||
}
|
||||
scrollToFiltersSmooth();
|
||||
}
|
||||
|
||||
function renderActiveFilters() {
|
||||
if (!el.activeFilters) return;
|
||||
el.activeFilters.innerHTML = "";
|
||||
|
||||
const vendorsById = new Map(state.data.vendors.map(v => [v.id, v.name]));
|
||||
const categoriesById = new Map(state.data.categories.map(c => [c.id, c.name]));
|
||||
|
||||
const items = [];
|
||||
for (const id of state.selectedVendors) {
|
||||
const name = vendorsById.get(id);
|
||||
if (name) items.push({ kind: "Вендор", name, id, type: "vendor" });
|
||||
}
|
||||
for (const id of state.selectedCategories) {
|
||||
const name = categoriesById.get(id);
|
||||
if (name) items.push({ kind: "Категория", name, id, type: "category" });
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
el.activeFilters.style.display = "none";
|
||||
return;
|
||||
}
|
||||
el.activeFilters.style.display = "flex";
|
||||
|
||||
for (const item of items) {
|
||||
const node = document.createElement("div");
|
||||
node.className = "active-filter";
|
||||
|
||||
const text = document.createElement("span");
|
||||
text.innerHTML = `<span class="kind">${item.kind}:</span> ${item.name}`;
|
||||
node.appendChild(text);
|
||||
|
||||
const remove = document.createElement("button");
|
||||
remove.className = "remove";
|
||||
remove.type = "button";
|
||||
remove.setAttribute("aria-label", `Убрать фильтр ${item.name}`);
|
||||
remove.textContent = "×";
|
||||
remove.addEventListener("click", () => {
|
||||
if (item.type === "vendor") state.selectedVendors.delete(item.id);
|
||||
else state.selectedCategories.delete(item.id);
|
||||
render();
|
||||
scrollToFiltersSmooth();
|
||||
});
|
||||
node.appendChild(remove);
|
||||
|
||||
el.activeFilters.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadScopeData(scope) {
|
||||
const res = await fetch(`/api/data?scope=${encodeURIComponent(scope)}`);
|
||||
state.data = await res.json();
|
||||
@@ -258,6 +345,7 @@
|
||||
el.vendorSearch.value = "";
|
||||
el.categorySearch.value = "";
|
||||
render();
|
||||
scrollAfterDeselect();
|
||||
});
|
||||
|
||||
el.modeInfra.addEventListener("click", () => {
|
||||
|
||||
Reference in New Issue
Block a user