NODEDC_BIM_VIEWER/frontend/src/ui/templatesMenu.js

170 lines
5.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.

export function createTemplatesMenu({ panel, fetchProjects, openProject, onShow, onHide }) {
let state = { templates: [], projects: [], loading: false, error: null };
const setState = (next) => {
state = { ...state, ...next };
render();
};
const renderEmpty = (parent, text) => {
const empty = document.createElement("div");
empty.className = "templates-empty";
empty.textContent = text;
parent.appendChild(empty);
};
const renderSection = (title, items, emptyText) => {
const section = document.createElement("div");
section.className = "templates-section";
const subtitle = document.createElement("div");
subtitle.className = "templates-subtitle";
subtitle.textContent = title;
section.appendChild(subtitle);
if (!items || !items.length) {
renderEmpty(section, emptyText);
} else {
items.forEach((item) => {
const card = document.createElement("button");
card.className = "example-card template-card";
card.type = "button";
card.addEventListener("click", () => openProject?.(item.id, item.type));
const titleEl = document.createElement("div");
titleEl.className = "card-title";
titleEl.textContent = item.name || item.id;
card.appendChild(titleEl);
const meta = document.createElement("div");
meta.className = "card-meta";
const typePill = document.createElement("span");
typePill.className = "card-pill";
typePill.dataset.kind = item.type || "project";
typePill.textContent = item.type === "template" ? "Шаблон" : "Проект";
meta.appendChild(typePill);
if (item.updatedAt) {
const updated = document.createElement("span");
updated.textContent = new Date(item.updatedAt).toLocaleDateString();
meta.appendChild(updated);
}
card.appendChild(meta);
section.appendChild(card);
});
}
return section;
};
const render = () => {
if (!panel) return;
panel.innerHTML = "";
const header = document.createElement("div");
header.className = "templates-header";
header.innerHTML = `<span class="templates-title">Проекты и шаблоны</span>`;
const actions = document.createElement("div");
actions.className = "templates-actions";
const refresh = document.createElement("button");
refresh.className = "templates-close";
refresh.type = "button";
refresh.title = "Обновить список";
refresh.setAttribute("aria-label", "Обновить список");
refresh.innerHTML = `
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
<path d="M12.2 5.3A4.4 4.4 0 0 0 4.7 3.4L3.6 4.5M3.7 2.1v2.4h2.4M3.8 10.7a4.4 4.4 0 0 0 7.5 1.9l1.1-1.1M12.3 13.9v-2.4H9.9" fill="none" stroke="currentColor" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
`;
refresh.addEventListener("click", () => load());
const closeBtn = document.createElement("button");
closeBtn.className = "templates-close";
closeBtn.type = "button";
closeBtn.setAttribute("aria-label", "Закрыть проекты и шаблоны");
closeBtn.innerHTML = `
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
</svg>
`;
closeBtn.addEventListener("click", hide);
actions.appendChild(refresh);
actions.appendChild(closeBtn);
header.appendChild(actions);
panel.appendChild(header);
const body = document.createElement("div");
body.className = "templates-panel-body";
panel.appendChild(body);
if (state.loading) {
const loadingEl = document.createElement("div");
loadingEl.className = "templates-empty";
loadingEl.textContent = "Загрузка списка проектов...";
body.appendChild(loadingEl);
return;
}
if (state.error) {
const errorEl = document.createElement("div");
errorEl.className = "templates-empty";
errorEl.style.color = "var(--error)";
errorEl.textContent = state.error;
body.appendChild(errorEl);
}
body.appendChild(renderSection("Шаблоны", state.templates, "Нет шаблонов"));
body.appendChild(renderSection("Мои проекты", state.projects, "Нет сохранённых проектов"));
};
const load = async () => {
if (!fetchProjects) return;
setState({ loading: true, error: null });
try {
const data = await fetchProjects();
setState({
loading: false,
templates: data?.templates || [],
projects: data?.projects || [],
error: null
});
} catch (err) {
setState({ loading: false, error: err?.message || "Не удалось загрузить проекты" });
}
};
const hide = () => {
if (panel) {
panel.classList.add("hidden");
panel.style.display = "";
}
onHide?.();
};
const show = () => {
render();
if (panel) {
panel.classList.remove("hidden");
panel.style.display = "flex";
}
load();
onShow?.();
};
const toggle = () => {
if (!panel) return;
if (panel.classList.contains("hidden")) {
show();
} else {
hide();
}
};
const setData = (data = {}) => {
setState({
templates: data.templates || [],
projects: data.projects || [],
loading: false,
error: data.error || null
});
};
return { render, show, hide, toggle, reload: load, setData };
}