Learn: section actions on the title row, scrollable tables, copy code

This commit is contained in:
2026-09-17 20:16:35 +03:00
parent 65b94f11cb
commit 6b1213f4cf
7 changed files with 189 additions and 67 deletions

View File

@@ -218,7 +218,21 @@ dialog.modal::backdrop { background: rgba(0,0,0,0.55); }
.md-body details.spoiler { border: 1px solid var(--c-border); border-radius: 0.5rem; padding: 0.5rem 0.75rem; margin: 0.5rem 0; }
.md-body details.spoiler summary { cursor: pointer; font-weight: 500; color: var(--c-text-link); }
.md-body img { max-width: 100%; }
.md-body table { margin: 0.5rem 0; }
.md-body table { margin: 0; }
/* Таблицы в статьях прокручиваются по горизонтали, как блоки кода */
.table-scroll { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0.5rem 0; }
/* Блок кода: кнопка копирования в правом верхнем углу */
.md-code-wrap { position: relative; margin: 0.5rem 0; }
.md-code-wrap pre { margin: 0; }
.md-copy {
position: absolute; top: 0.4rem; right: 0.4rem;
font: inherit; font-size: 0.72rem; line-height: 1;
padding: 0.25rem 0.5rem; cursor: pointer;
color: var(--c-text-muted); background: var(--c-surface);
border: 1px solid var(--c-border); border-radius: 0.35rem; opacity: 0.85;
}
.md-copy:hover { color: var(--c-text); opacity: 1; }
.md-copy.copied { color: var(--c-text-link); }
/* Empty state */
.empty { text-align: center; color: var(--c-text-muted); padding: 1.5rem; }
@@ -253,12 +267,22 @@ code.code { padding: 0.125rem 0.5rem; border-radius: 0.25rem; font-family: monos
.sort-list { list-style: none; margin: 0; padding: 0; }
.sort-list > li { display: flex; align-items: center; gap: 0.5rem; min-width: 0; padding: 0.15rem 0; }
.chapter-card { padding: 0.75rem 1rem; }
details.sub-acc { margin-top: 0.25rem; border-top: 1px solid var(--c-border); }
[data-section-list] > details.sub-acc:first-of-type { border-top: none; }
details.sub-acc > summary.sub-head { list-style: none; cursor: pointer; padding: 0.4rem 0; }
details.sub-acc > summary::-webkit-details-marker { display: none; }
.sub-arrow { flex: 0 0 auto; width: 8px; height: 8px; border-right: 2px solid var(--c-text-muted); border-bottom: 2px solid var(--c-text-muted); transform: rotate(45deg); transition: transform 0.2s; }
details.sub-acc[open] > summary .sub-arrow { transform: rotate(-135deg); }
.sub-acc { margin-top: 0.25rem; border-top: 1px solid var(--c-border); }
[data-section-list] > .sub-acc:first-of-type { border-top: none; }
.sub-acc > .sub-head { padding: 0.4rem 0; }
.sub-acc.collapsed > .section-body { display: none; }
.sub-toggle {
display: inline-flex; align-items: center; justify-content: center;
flex: 0 0 auto; width: 1rem; height: 1rem; padding: 0; border: none;
background: none; cursor: pointer; color: var(--c-text-muted);
}
.sub-toggle:hover { color: var(--c-text); }
.sub-arrow {
flex: 0 0 auto; width: 8px; height: 8px;
border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
transform: rotate(-135deg); transition: transform 0.2s;
}
.sub-acc.collapsed .sub-arrow { transform: rotate(45deg); }
.section-body { padding-bottom: 0.4rem; }
/* ---- Матрица группы и карточки защиты ---- */

View File

@@ -123,17 +123,22 @@
autoExpand(hoverHost(target));
}
/* Пока тянем, раскрываем закрытую главу/раздел под курсором, чтобы внутрь можно было бросить. */
/* Пока тянем, раскрываем свёрнутый раздел под курсором, чтобы внутрь можно было бросить. */
function hoverHost(target) {
return target.closest('details[data-chapter-id], details[data-section-id]');
return target.closest('.sub-acc[data-section-id]');
}
function autoExpand(host) {
if (!host || host.open || host === state.element) return;
if (!host || host === state.element) return;
if (!host.classList.contains('collapsed')) return;
if (hoverTarget === host) return;
hoverTarget = host;
clearTimeout(hoverTimer);
hoverTimer = setTimeout(function () { host.open = true; }, 600);
hoverTimer = setTimeout(function () {
host.classList.remove('collapsed');
var toggle = host.querySelector('.sub-toggle');
if (toggle) toggle.setAttribute('aria-expanded', 'true');
}, 600);
}
function onDrop(event) {

View File

@@ -0,0 +1,80 @@
/* Блоки кода в материалах: кнопка «Копировать» в углу и копирование по клику на блок. */
(function () {
function fallbackCopy(text) {
var area = document.createElement('textarea');
area.value = text;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.top = '-1000px';
document.body.appendChild(area);
area.select();
try { document.execCommand('copy'); } catch (e) { /* игнорируем */ }
document.body.removeChild(area);
}
function copyText(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text).catch(function () { fallbackCopy(text); });
}
fallbackCopy(text);
return Promise.resolve();
}
function codeText(pre) {
var code = pre.querySelector('code');
if (code) return code.innerText.replace(/\n+$/, '');
var clone = pre.cloneNode(true);
clone.querySelectorAll('.md-copy').forEach(function (btn) { btn.remove(); });
return clone.textContent.replace(/\n+$/, '');
}
function flash(btn) {
if (btn.dataset.busy) return;
btn.dataset.busy = '1';
var original = btn.textContent;
btn.textContent = 'Скопировано';
btn.classList.add('copied');
setTimeout(function () {
btn.textContent = original;
btn.classList.remove('copied');
delete btn.dataset.busy;
}, 1200);
}
function enhance(pre) {
if (pre.dataset.copyReady) return;
pre.dataset.copyReady = '1';
var wrap = document.createElement('div');
wrap.className = 'md-code-wrap';
pre.parentNode.insertBefore(wrap, pre);
wrap.appendChild(pre);
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'md-copy';
btn.textContent = 'Копировать';
btn.title = 'Скопировать код';
btn.setAttribute('aria-label', 'Скопировать код');
btn.addEventListener('click', function (event) {
event.preventDefault();
event.stopPropagation();
copyText(codeText(pre)).then(function () { flash(btn); });
});
wrap.appendChild(btn);
pre.addEventListener('click', function () {
copyText(codeText(pre)).then(function () { flash(btn); });
});
}
function ready() {
document.querySelectorAll('.md-body pre').forEach(enhance);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', ready);
} else {
ready();
}
})();