1
0
Fork 0
mirror of https://github.com/EDeev/deev.space.git synced 2026-10-07 20:49:59 +03:00
deev.space/static/js/skills.js
Egor Deev 878b4580e4 Версия 2.0: код с продакшена deev.space
Перенесено всё, что накопилось на сервере с декабря 2025:
- анонимный посетитель по подписанной cookie (main/middleware.py), лайки и дизлайки
  без регистрации с антинакруткой, уникальные просмотры статей;
- карточка-паспорт, стек в одну строку, блок «В цифрах»;
- миграции 0005–0008, команды load_initial_data и populate_demo, robots.txt;
- обновлённые шаблоны, стили и скрипты.

Безопасность и порядок:
- все секреты только из окружения, значения по умолчанию пустые;
- DEBUG по умолчанию выключен, без DJANGO_SECRET_KEY запуск в бою невозможен;
- ALLOWED_HOSTS, путь к базе и SSL-редирект настраиваются переменными;
- убрана статика админки Django (результат collectstatic) и media из git;
- добавлены .gitignore и .gitattributes (переводы строк — LF).
2026-10-02 11:58:54 +00:00

54 lines
2.3 KiB
JavaScript

/* Стек в одну строку: что не влезает по ширине — прячется за «+N». */
(function () {
function fit(box) {
const tags = Array.from(box.querySelectorAll('.skill-tag'));
const btn = box.querySelector('.skills-more-btn');
tags.forEach(t => t.classList.remove('is-off'));
if (box.classList.contains('is-open')) {
btn.hidden = false;
btn.textContent = 'свернуть';
return;
}
btn.hidden = true;
const gap = parseFloat(getComputedStyle(box).columnGap) || 0;
const avail = box.clientWidth;
const widths = tags.map(t => t.getBoundingClientRect().width);
const total = widths.reduce((a, w) => a + w, 0) + gap * Math.max(tags.length - 1, 0);
if (total <= avail + 0.5) return;
btn.hidden = false;
let used = 0, shown = 0;
for (let i = 0; i < tags.length; i++) {
btn.textContent = '+' + (tags.length - i - 1);
const need = used + (i ? gap : 0) + widths[i] + gap + btn.getBoundingClientRect().width;
if (need > avail + 0.5) break;
used += (i ? gap : 0) + widths[i];
shown = i + 1;
}
shown = Math.max(shown, 1);
tags.forEach((t, i) => t.classList.toggle('is-off', i >= shown));
btn.textContent = '+' + (tags.length - shown);
}
function init() {
document.querySelectorAll('.skills-rows').forEach(root => {
if (root.dataset.ready) return;
root.dataset.ready = '1';
const boxes = Array.from(root.querySelectorAll('.skills-row-tags'));
const refit = () => boxes.forEach(fit);
root.addEventListener('click', e => {
const b = e.target.closest('.skills-more-btn');
if (!b) return;
b.parentElement.classList.toggle('is-open');
fit(b.parentElement);
});
if ('ResizeObserver' in window) new ResizeObserver(refit).observe(root);
else window.addEventListener('resize', refit);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(refit);
refit();
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();