Implement updated Bastion prototype and search workflow

This commit is contained in:
KrivovDE
2026-09-09 15:42:13 +03:00
parent 28f0852847
commit 4af653de9c
143 changed files with 14540 additions and 99 deletions

42
okb-source/site.js Normal file
View File

@@ -0,0 +1,42 @@
(() => {
const modal = document.querySelector('#detailModal');
const viewport = document.querySelector('#modalViewport');
const renderCharts = root => root.querySelectorAll('[data-chart]').forEach(host => {
if (host.dataset.rendered) return;
host.dataset.rendered = 'true';
const data = JSON.parse(host.dataset.chart);
const render = (title, series, color, unit = 'тыс. руб.') => {
const width = 520, height = 252, pad = {l: 42, r: 18, t: 18, b: 36};
const values = series.map(item => item.value), maximum = Math.max(...values) * 1.15, minimum = Math.min(0, ...values) * .85;
const x = index => pad.l + index * ((width - pad.l - pad.r) / Math.max(1, series.length - 1));
const y = value => pad.t + (maximum - value) / (maximum - minimum || 1) * (height - pad.t - pad.b);
const points = series.map((item, index) => `${x(index)},${y(item.value)}`).join(' ');
const area = `${pad.l},${height - pad.b} ${points} ${x(series.length - 1)},${height - pad.b}`;
const grid = [0, .25, .5, .75, 1].map(t => { const yy = pad.t + t * (height - pad.t - pad.b); return `<line class="grid-line" x1="${pad.l}" y1="${yy}" x2="${width-pad.r}" y2="${yy}"/>`; }).join('');
const labels = series.map((item, index) => `<text class="chart-label" x="${x(index)}" y="${height-12}" text-anchor="middle">${item.year}</text>`).join('');
const dots = series.map((item, index) => `<circle class="chart-point" cx="${x(index)}" cy="${y(item.value)}" r="5"><title>${item.year}: ${item.value.toLocaleString('ru-RU')} ${unit}</title></circle>`).join('');
return `<article class="line-chart" style="--chart:${color}"><h3>${title}</h3><svg viewBox="0 0 ${width} ${height}" role="img" aria-label="${title}">${grid}<polygon class="chart-area" points="${area}"/><polyline class="chart-line" points="${points}"/>${dots}${labels}</svg></article>`;
};
if (data.employees) {
host.innerHTML = render('Динамика численности', data.employees, '#BE7AB9', data.unit || 'чел.');
return;
}
const unit = data.unit || 'тыс. руб.';
host.innerHTML = render(`Выручка, ${unit}`, data.income, '#73b8e5', unit) + render(`Чистая прибыль, ${unit}`, data.profit, '#55bd79', unit);
});
document.addEventListener('click', event => {
const trigger = event.target.closest('.detail-button');
if (trigger) {
const template = document.querySelector(`#${CSS.escape(trigger.dataset.modalSource)}`);
if (!template) return;
viewport.replaceChildren(template.content.cloneNode(true));
renderCharts(viewport);
modal.showModal();
return;
}
if (event.target.closest('.modal-close')) modal.close();
if (event.target === modal) modal.close();
});
document.addEventListener('keydown', event => { if (event.key === 'Escape' && modal?.open) modal.close(); });
renderCharts(document);
})();