Initial import of NODEDC BIM Viewer

This commit is contained in:
CODEX
2026-06-04 13:25:42 +03:00
commit 9aaf82862f
2075 changed files with 3592575 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
export function createSliderControl({ label = "", value = 0, min = 0, max = 100, step = 1, onChange }) {
const row = document.createElement("div");
row.className = "slider-row";
const lbl = document.createElement("label");
lbl.textContent = label;
lbl.className = "slider-label";
const input = document.createElement("input");
input.type = "range";
input.className = "slider-input";
input.min = min;
input.max = max;
input.step = step;
input.value = value;
const updateFill = () => {
const percent = ((input.value - input.min) / (input.max - input.min)) * 100;
input.style.setProperty("--slider-percent", `${percent}%`);
};
input.addEventListener("input", () => {
updateFill();
const v = Number(input.value);
onChange?.(v);
});
updateFill();
row.appendChild(lbl);
row.appendChild(input);
return row;
}
+24
View File
@@ -0,0 +1,24 @@
export function createLogo({
size = 160, // ширина контейнера (px)
aspect = 4, // соотношение width / height контейнера
offsetY = 0,
paddingX = 0,
imageUrl = "./assets/logo.svg",
} = {}) {
const wrapper = document.createElement("div");
wrapper.className = "ue-logo";
wrapper.style.width = `${size}px`;
wrapper.style.height = `${size / aspect}px`;
wrapper.style.marginLeft = `${paddingX}px`;
wrapper.style.marginRight = `${paddingX}px`;
wrapper.style.transform = `translateY(${offsetY}px)`;
const img = document.createElement("img");
img.src = imageUrl;
img.alt = "logo";
img.style.width = "100%";
img.style.height = "100%";
img.style.objectFit = "contain";
img.style.display = "block";
wrapper.appendChild(img);
return wrapper;
}
+90
View File
@@ -0,0 +1,90 @@
export function createMeasurementModal({ panel, backdrop, onDelete, onToggleVisibility, onClose }) {
let activeMeasurementId = null;
let currentVisible = true;
const hide = () => {
if (panel) {
panel.classList.add("hidden");
panel.style.display = "";
}
if (backdrop) {
backdrop.classList.add("hidden");
backdrop.style.display = "";
}
activeMeasurementId = null;
onClose?.();
};
const render = () => {
if (!panel) return;
panel.innerHTML = "";
const header = document.createElement("div");
header.className = "templates-header";
header.innerHTML = `<span class="templates-title">Размер</span>`;
const closeBtn = document.createElement("button");
closeBtn.className = "templates-close";
closeBtn.textContent = "×";
closeBtn.addEventListener("click", hide);
header.appendChild(closeBtn);
panel.appendChild(header);
const list = document.createElement("div");
list.className = "template-list";
panel.appendChild(list);
const deleteBtn = document.createElement("button");
deleteBtn.className = "example-card";
deleteBtn.textContent = "Удалить размер";
deleteBtn.addEventListener("click", () => {
if (!activeMeasurementId) return;
onDelete?.(activeMeasurementId);
});
list.appendChild(deleteBtn);
const toggleBtn = document.createElement("button");
toggleBtn.className = "example-card";
toggleBtn.textContent = currentVisible ? "Скрыть размер" : "Показать размер";
toggleBtn.addEventListener("click", () => {
if (!activeMeasurementId) return;
const nextVisible = !currentVisible;
onToggleVisibility?.(activeMeasurementId, nextVisible);
currentVisible = nextVisible;
toggleBtn.textContent = currentVisible ? "Скрыть размер" : "Показать размер";
});
list.appendChild(toggleBtn);
};
const show = ({ measurementId, visible = true } = {}) => {
activeMeasurementId = measurementId || null;
currentVisible = visible !== false;
render();
if (panel) {
panel.classList.remove("hidden");
panel.style.display = "grid";
}
if (backdrop) {
backdrop.classList.remove("hidden");
backdrop.style.display = "block";
}
};
const setVisibility = (visible) => {
currentVisible = !!visible;
render();
if (panel && !panel.classList.contains("hidden")) {
panel.style.display = "grid";
}
};
if (backdrop) {
backdrop.addEventListener("click", hide);
}
return {
show,
hide,
setVisibility,
};
}
+75
View File
@@ -0,0 +1,75 @@
export function createObjectModal({ panel, backdrop, onDelete, onClose }) {
let activeTarget = null;
const hide = () => {
if (panel) {
panel.classList.add("hidden");
panel.style.display = "";
}
if (backdrop) {
backdrop.classList.add("hidden");
backdrop.style.display = "";
}
activeTarget = null;
onClose?.();
};
const render = () => {
if (!panel) return;
panel.innerHTML = "";
const header = document.createElement("div");
header.className = "templates-header";
header.innerHTML = `<span class="templates-title">Объект</span>`;
const closeBtn = document.createElement("button");
closeBtn.className = "templates-close";
closeBtn.textContent = "×";
closeBtn.addEventListener("click", hide);
header.appendChild(closeBtn);
panel.appendChild(header);
const list = document.createElement("div");
list.className = "template-list";
panel.appendChild(list);
if (activeTarget?.label) {
const label = document.createElement("div");
label.className = "muted";
label.style.margin = "0 0 8px";
label.textContent = activeTarget.label;
list.appendChild(label);
}
const deleteBtn = document.createElement("button");
deleteBtn.className = "example-card danger-btn";
deleteBtn.textContent = "Удалить объект";
deleteBtn.addEventListener("click", () => {
if (!activeTarget?.modelId) return;
onDelete?.(activeTarget);
});
list.appendChild(deleteBtn);
};
const show = ({ modelId, label } = {}) => {
activeTarget = { modelId, label };
render();
if (panel) {
panel.classList.remove("hidden");
panel.style.display = "grid";
}
if (backdrop) {
backdrop.classList.remove("hidden");
backdrop.style.display = "block";
}
};
if (backdrop) {
backdrop.addEventListener("click", hide);
}
return {
show,
hide,
};
}
+401
View File
@@ -0,0 +1,401 @@
import { createSliderControl } from "./controls/slider.js";
const defaultSettings = {
themeVersion: "v3-yellow",
bgOuter: "#0f0f0f",
canvasTop: "#1c1c1c",
canvasBottom: "#1c1c1c",
loadingTop: "#1c1c1c",
loadingBottom: "#1c1c1c",
accent: "#ffea00",
accent2: "#ffea00",
templateFill: "#1c1c1c",
templateOutline: "#233044",
templateOutlineHover: "rgba(255,234,0,0.7)",
projectBtnFill: "#1c1c1c",
projectBtnBorder: "#233044",
projectBtnHover: "rgba(255,234,0,0.7)",
modalBtnPrimary: "#ffea00",
modalBtnSecondary: "#292929",
loaderColor: "#ffea00",
templateGlow: 80,
toolbarBg: "#1c1c1c",
toolbarBgActive: "#ffea00",
toolbarBorder: "#233044",
toolbarOutline: "#ffea00",
modalBg: "#1c1c1c",
modalBgAlpha: 1,
modalFocus: "#292929",
modalFocusAlpha: 1,
modalBorder: "#292929",
modalElement: "#292929",
modalElementOutline: "#2b2b2b",
modalRadius: "14px",
border: "#233044",
text: "#e6edf3",
panel: "#1c1c1c",
panel2: "#292929",
highlight: "#ffea00",
cubeText: "#242424",
cubeEdges: "#e0e7ff",
plus: "#ffea00",
navCubeBase: "#ffea00",
navCubeHover: "#ffffff",
navCubeText: "#242424",
navCubeEdge: "#e0e7ff",
navCubeEdgeAlpha: 0.6,
measureLine: "#ffea00",
measureLabel: "#ffea00",
measureLabelText: "#292929",
measureDot: "#ffea00",
};
const STORAGE_KEY = "bimdc-settings-v3";
const LEGACY_KEYS = ["bimdc-settings", "bimdc-settings-v2"];
const projectSettings = typeof window !== "undefined" && window.__BIMDC_THEME__
? { ...defaultSettings, ...window.__BIMDC_THEME__ }
: { ...defaultSettings };
const applySettings = (s) => {
const root = document.documentElement;
root.style.setProperty("--bg-outer", s.bgOuter);
root.style.setProperty("--canvas-top", s.canvasTop);
root.style.setProperty("--canvas-bottom", s.canvasBottom);
root.style.setProperty("--loading-top", s.loadingTop);
root.style.setProperty("--loading-bottom", s.loadingBottom);
root.style.setProperty("--accent", s.accent);
root.style.setProperty("--accent-2", s.accent2);
root.style.setProperty("--template-fill", s.templateFill);
root.style.setProperty("--template-outline", s.templateOutline);
root.style.setProperty("--template-outline-hover", s.templateOutlineHover);
root.style.setProperty("--project-btn-fill", s.projectBtnFill);
root.style.setProperty("--project-btn-border", s.projectBtnBorder);
root.style.setProperty("--project-btn-hover", s.projectBtnHover);
root.style.setProperty("--modal-btn-primary", s.modalBtnPrimary);
root.style.setProperty("--modal-btn-secondary", s.modalBtnSecondary);
root.style.setProperty("--loader-color", s.loaderColor);
root.style.setProperty("--template-glow-strength", `${s.templateGlow}%`);
root.style.setProperty("--toolbar-bg", s.toolbarBg);
root.style.setProperty("--toolbar-bg-active", s.toolbarBgActive);
root.style.setProperty("--toolbar-border", s.toolbarBorder);
root.style.setProperty("--toolbar-outline", s.toolbarOutline);
const bgAlphaPct = `${(s.modalBgAlpha ?? 1) * 100}%`;
const focusAlphaPct = `${(s.modalFocusAlpha ?? 1) * 100}%`;
root.style.setProperty("--modal-bg", s.modalBg);
root.style.setProperty("--modal-bg-alpha", bgAlphaPct);
root.style.setProperty("--modal-focus", s.modalFocus);
root.style.setProperty("--modal-focus-alpha", focusAlphaPct);
root.style.setProperty("--modal-border", s.modalBorder);
root.style.setProperty("--modal-element", s.modalElement);
root.style.setProperty("--modal-element-outline", s.modalElementOutline);
root.style.setProperty("--modal-radius", s.modalRadius);
root.style.setProperty("--border", s.border);
root.style.setProperty("--text", s.text);
root.style.setProperty("--panel", s.panel);
root.style.setProperty("--panel-2", s.panel2);
root.style.setProperty("--highlight-color", s.highlight);
root.style.setProperty("--cube-text", s.cubeText);
root.style.setProperty("--cube-edges", s.navCubeBase || s.cubeEdges);
root.style.setProperty("--cube-base", s.navCubeBase || s.cubeEdges);
root.style.setProperty("--cube-hover", s.navCubeHover);
root.style.setProperty("--cube-text", s.navCubeText || s.cubeText);
root.style.setProperty("--cube-edge-color", s.navCubeEdge);
root.style.setProperty("--cube-edge-alpha", s.navCubeEdgeAlpha);
root.style.setProperty("--plus-color", s.plus);
root.style.setProperty("--template-fill", s.templateFill);
root.style.setProperty("--template-outline", s.templateOutline);
root.style.setProperty("--measure-line", s.measureLine);
root.style.setProperty("--measure-label", s.measureLabel);
root.style.setProperty("--measure-label-text", s.measureLabelText);
root.style.setProperty("--measure-dot", s.measureDot);
// подсветка объектов
root.style.setProperty("--highlight-color", s.highlight);
};
const saveSettings = (s) => {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
} catch (e) {
// ignore
}
};
const loadSettings = () => {
try {
// purge legacy keys so старые палитры не мешали
LEGACY_KEYS.forEach((k) => localStorage.removeItem(k));
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) {
// зашиваем проектные настройки обратно в storage, чтобы при чистом кэше они сразу подхватывались
saveSettings(projectSettings);
return { ...projectSettings };
}
const stored = JSON.parse(raw);
if (stored.themeVersion && stored.themeVersion !== projectSettings.themeVersion) {
saveSettings(projectSettings);
return { ...projectSettings };
}
return { ...projectSettings, ...stored };
} catch (e) {
saveSettings(projectSettings);
return { ...projectSettings };
}
};
export function createSettingsMenu({ panel, controls = {}, onChange = null, onShow, onHide }) {
let state = loadSettings();
applySettings(state);
const getState = () => ({ ...state });
const applyStateExternal = (nextState = {}) => {
state = { ...state, ...nextState };
applySettings(state);
saveSettings(state);
if (typeof onChange === "function") {
onChange({ key: null, state });
}
if (panel && !panel.classList.contains("hidden")) {
render();
}
};
const hide = () => {
if (panel) {
panel.classList.add("hidden");
panel.style.display = "";
}
onHide?.();
};
const setVal = (key, val) => {
state = { ...state, [key]: val };
applySettings(state);
saveSettings(state);
if (typeof onChange === "function") {
onChange({ key, value: val, state });
}
};
const render = () => {
if (!panel) return;
panel.innerHTML = "";
const header = document.createElement("div");
header.className = "settings-header";
header.innerHTML = `<span class="settings-title">Настройки</span>`;
const closeBtn = document.createElement("button");
closeBtn.className = "settings-close";
closeBtn.textContent = "—";
closeBtn.addEventListener("click", hide);
header.appendChild(closeBtn);
panel.appendChild(header);
const section = (title) => {
const box = document.createElement("div");
box.className = "settings-section";
const t = document.createElement("div");
t.className = "section-title";
t.textContent = title;
box.appendChild(t);
return box;
};
const addColor = (parent, label, key) => {
const isHex = (val) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test((val || "").trim());
const row = document.createElement("div");
row.className = "color-row";
const lbl = document.createElement("label");
lbl.textContent = label;
const input = document.createElement("input");
input.type = "color";
input.value = isHex(state[key]) ? state[key] : "#ffffff";
const hex = document.createElement("input");
hex.type = "text";
hex.value = state[key];
input.addEventListener("change", (e) => {
hex.value = e.target.value;
setVal(key, e.target.value);
});
hex.addEventListener("change", (e) => {
input.value = e.target.value;
setVal(key, e.target.value);
});
row.appendChild(lbl);
row.appendChild(input);
row.appendChild(hex);
parent.appendChild(row);
};
const font = section("Фон");
addColor(font, "Фон внешний", "bgOuter");
addColor(font, "Фон холста верх", "canvasTop");
addColor(font, "Фон холста низ", "canvasBottom");
addColor(font, "Фон загрузки верх", "loadingTop");
addColor(font, "Фон загрузки низ", "loadingBottom");
panel.appendChild(font);
const buttons = section("Кнопки");
addColor(buttons, "Плюс", "plus");
addColor(buttons, "Кнопки (заливка)", "templateFill");
addColor(buttons, "Кнопки (бордер)", "templateOutline");
addColor(buttons, "Кнопки (outline hover)", "templateOutlineHover");
const brightnessSlider = createSliderControl({
label: "Яркость подсветки",
value: Number(state.templateGlow ?? 80),
min: 0,
max: 100,
step: 1,
onChange: (val) => setVal("templateGlow", val),
});
buttons.appendChild(brightnessSlider);
panel.appendChild(buttons);
const projectButtons = section("Кнопки проектов");
addColor(projectButtons, "Проекты (заливка)", "projectBtnFill");
addColor(projectButtons, "Проекты (бордер)", "projectBtnBorder");
addColor(projectButtons, "Проекты (hover)", "projectBtnHover");
panel.appendChild(projectButtons);
const modalButtons = section("Кнопки модалок");
addColor(modalButtons, "Модальная primary", "modalBtnPrimary");
addColor(modalButtons, "Модальная secondary", "modalBtnSecondary");
addColor(modalButtons, "Лоудер", "loaderColor");
panel.appendChild(modalButtons);
const toolbar = section("Toolbar");
addColor(toolbar, "Фон", "toolbarBg");
addColor(toolbar, "Актив", "toolbarBgActive");
addColor(toolbar, "Бордер", "toolbarBorder");
addColor(toolbar, "Outline", "toolbarOutline");
panel.appendChild(toolbar);
const navCube = section("NavCube");
addColor(navCube, "Грани", "navCubeBase");
addColor(navCube, "Hover", "navCubeHover");
addColor(navCube, "Текст", "navCubeText");
addColor(navCube, "Рёбра", "navCubeEdge");
const edgeAlphaRow = document.createElement("div");
edgeAlphaRow.className = "color-row";
const edgeAlphaLbl = document.createElement("label");
edgeAlphaLbl.textContent = "Прозрачность рёбер (0-1)";
const edgeAlphaInput = document.createElement("input");
edgeAlphaInput.type = "number";
edgeAlphaInput.step = "0.05";
edgeAlphaInput.min = "0";
edgeAlphaInput.max = "1";
edgeAlphaInput.value = state.navCubeEdgeAlpha;
edgeAlphaInput.addEventListener("change", () => {
const v = Math.min(1, Math.max(0, parseFloat(edgeAlphaInput.value) || 0));
edgeAlphaInput.value = v;
setVal("navCubeEdgeAlpha", v);
});
edgeAlphaRow.appendChild(edgeAlphaLbl);
edgeAlphaRow.appendChild(edgeAlphaInput);
edgeAlphaRow.appendChild(document.createElement("div"));
navCube.appendChild(edgeAlphaRow);
panel.appendChild(navCube);
const measure = section("Линейка");
addColor(measure, "Линия/бордер", "measureLine");
addColor(measure, "Плашка", "measureLabel");
addColor(measure, "Текст плашки", "measureLabelText");
addColor(measure, "Кружок", "measureDot");
panel.appendChild(measure);
const viewport = section("Viewport");
const addCheck = (label, checked, handler) => {
const row = document.createElement("div");
row.className = "checkbox-row";
const chk = document.createElement("input");
chk.type = "checkbox";
chk.checked = checked;
chk.addEventListener("change", () => handler(chk.checked));
const lbl = document.createElement("label");
lbl.textContent = label;
row.appendChild(chk);
row.appendChild(lbl);
viewport.appendChild(row);
};
if (controls.toggleEdges) addCheck("Рёбра", controls.toggleEdges.checked, (v) => { controls.toggleEdges.checked = v; controls.toggleEdges.dispatchEvent(new Event("change")); });
if (controls.toggleTransparent) addCheck("Прозрачный фон", controls.toggleTransparent.checked, (v) => { controls.toggleTransparent.checked = v; controls.toggleTransparent.dispatchEvent(new Event("change")); });
if (controls.addMode) addCheck("Добавлять в сцену", controls.addMode.checked, (v) => { controls.addMode.checked = v; });
panel.appendChild(viewport);
const modals = section("Модальные окна");
addColor(modals, "Основной фон", "modalBg");
addColor(modals, "Фокусный фон", "modalFocus");
const modalBgAlphaRow = createSliderControl({
label: "Прозрачность основного фона",
min: 0,
max: 1,
step: 0.05,
value: parseFloat(state.modalBgAlpha ?? 1),
onChange: (val) => {
const num = Math.max(0, Math.min(1, parseFloat(val)));
setVal("modalBgAlpha", num);
document.documentElement.style.setProperty("--modal-bg-alpha", `${num * 100}%`);
},
});
modals.appendChild(modalBgAlphaRow);
const modalFocusAlphaRow = createSliderControl({
label: "Прозрачность фокусного фона",
min: 0,
max: 1,
step: 0.05,
value: parseFloat(state.modalFocusAlpha ?? 1),
onChange: (val) => {
const num = Math.max(0, Math.min(1, parseFloat(val)));
setVal("modalFocusAlpha", num);
document.documentElement.style.setProperty("--modal-focus-alpha", `${num * 100}%`);
},
});
modals.appendChild(modalFocusAlphaRow);
addColor(modals, "Аутлайн", "modalBorder");
addColor(modals, "Фон элемента", "modalElement");
addColor(modals, "Аутлайн элемента", "modalElementOutline");
const radiusRow = document.createElement("div");
radiusRow.className = "color-row";
const radiusLbl = document.createElement("label");
radiusLbl.textContent = "Радиус";
const radiusInput = document.createElement("input");
radiusInput.type = "number";
radiusInput.value = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--modal-radius")) || 14;
radiusInput.addEventListener("change", () => {
const val = `${radiusInput.value}px`;
document.documentElement.style.setProperty("--modal-radius", val);
setVal("modalRadius", val);
});
radiusRow.appendChild(radiusLbl);
radiusRow.appendChild(radiusInput);
radiusRow.appendChild(document.createElement("div"));
modals.appendChild(radiusRow);
panel.appendChild(modals);
const cube = section("Выделение объектов");
addColor(cube, "Подсветка объектов", "highlight");
panel.appendChild(cube);
};
const show = () => {
render();
if (panel) {
panel.classList.remove("hidden");
panel.style.display = "grid";
}
onShow?.();
};
const toggle = () => {
if (!panel) return;
if (panel.classList.contains("hidden")) {
show();
} else {
hide();
}
};
return { render, show, toggle, hide, getState, applyState: applyStateExternal };
}
+154
View File
@@ -0,0 +1,154 @@
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.style.display = "flex";
actions.style.gap = "6px";
const refresh = document.createElement("button");
refresh.className = "templates-close";
refresh.title = "Обновить список";
refresh.textContent = "↻";
refresh.addEventListener("click", () => load());
const closeBtn = document.createElement("button");
closeBtn.className = "templates-close";
closeBtn.textContent = "×";
closeBtn.addEventListener("click", hide);
actions.appendChild(refresh);
actions.appendChild(closeBtn);
header.appendChild(actions);
panel.appendChild(header);
if (state.loading) {
const loadingEl = document.createElement("div");
loadingEl.className = "templates-empty";
loadingEl.textContent = "Загрузка списка проектов...";
panel.appendChild(loadingEl);
return;
}
if (state.error) {
const errorEl = document.createElement("div");
errorEl.className = "templates-empty";
errorEl.style.color = "var(--error)";
errorEl.textContent = state.error;
panel.appendChild(errorEl);
}
panel.appendChild(renderSection("Шаблоны", state.templates, "Нет шаблонов"));
panel.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 = "grid";
}
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 };
}
+20
View File
@@ -0,0 +1,20 @@
const toolbarConfig = {
size: 25,
gap: 10,
radius: 7,
paddingY: 6,
iconPadding: 5,
iconSize: 13,
};
const applyToolbarVars = () => {
const root = document.documentElement;
root.style.setProperty("--tb-size", `${toolbarConfig.size}px`);
root.style.setProperty("--tb-gap", `${toolbarConfig.gap}px`);
root.style.setProperty("--tb-radius", `${toolbarConfig.radius}px`);
root.style.setProperty("--tb-pad-y", `${toolbarConfig.paddingY}px`);
root.style.setProperty("--tb-icon-pad", `${toolbarConfig.iconPadding}px`);
root.style.setProperty("--tb-icon-size", `${toolbarConfig.iconSize}px`);
};
export { toolbarConfig, applyToolbarVars };