Article page: offcanvas-lg sidebar (sticky desktop, right slide mobile), compact action row; persistent accordion state (localStorage)
This commit is contained in:
@@ -598,6 +598,9 @@ dialog.dlg.dlg-lg .dlg-body { max-height: 88vh; min-height: 62vh; }
|
||||
/* Действия столбиком: ровные одинаковые кнопки друг под другом */
|
||||
.actions-col { display: inline-flex; flex-direction: column; gap: 0.35rem; align-items: stretch; min-width: 6.5rem; }
|
||||
.actions-col .inline-form { display: block; width: 100%; }
|
||||
/* Строки с кнопками-иконками: формы не растягиваются на всю ширину (иначе столбик на мобиле) */
|
||||
.action-row .inline-form { display: inline-flex; width: auto; flex-wrap: nowrap; }
|
||||
#articleSide { --bs-offcanvas-width: 17rem; --bs-offcanvas-bg: var(--c-surface); --bs-offcanvas-color: var(--c-text); --bs-offcanvas-border-color: var(--c-border); }
|
||||
.actions-col .btn { display: block; width: 100%; text-align: center; }
|
||||
/* Иконки — SVG-файлы из /icons через CSS-маски: цвет наследуется от текста */
|
||||
.cell-ico { font-size: 0.95rem; line-height: 1; }
|
||||
@@ -643,8 +646,10 @@ th.th-filterable { padding-bottom: 0.35rem; }
|
||||
}
|
||||
|
||||
/* Чтение материалов: контент + боковое содержание */
|
||||
.learn-layout { display: grid; grid-template-columns: 19rem minmax(0, 1fr); gap: 1.5rem; align-items: start; }
|
||||
.learn-side { position: sticky; top: 1rem; max-height: calc(100vh - 2rem); overflow: auto; font-size: 0.875rem; }
|
||||
@media (min-width: 992px) {
|
||||
.learn-side { position: sticky; top: 4.5rem; max-height: calc(100vh - 5.5rem); overflow: auto; }
|
||||
}
|
||||
.learn-side { font-size: 0.875rem; }
|
||||
.side-chapter { font-weight: 600; margin: 0.6rem 0 0.2rem; }
|
||||
.side-section { margin: 0.25rem 0 0.5rem 0.75rem; }
|
||||
.side-section > a { color: var(--c-text-muted); }
|
||||
@@ -656,14 +661,6 @@ th.th-filterable { padding-bottom: 0.35rem; }
|
||||
.article-nav .ml-auto, .article-nav a:last-child:not(:only-child) { margin-left: auto; }
|
||||
.article-nav .btn { min-width: 0; max-width: calc(50% - 0.4rem); overflow: hidden; white-space: nowrap; }
|
||||
.article-nav .btn > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
@media (max-width: 1000px) {
|
||||
/* minmax(0, 1fr), а не 1fr: иначе колонка растягивается до min-content
|
||||
длинных строк кода и таблиц и страница вылезает за экран */
|
||||
.learn-layout { grid-template-columns: minmax(0, 1fr); }
|
||||
.learn-layout > * { min-width: 0; }
|
||||
.learn-main { order: 1; min-width: 0; }
|
||||
.learn-side { position: static; max-height: none; order: 2; min-width: 0; }
|
||||
}
|
||||
|
||||
/* Панель форматирования markdown и встроенный редактор */
|
||||
.md-toolbar {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
function sectionStateKey(id) { return 'lms-section-open:' + id; }
|
||||
function applySectionState(acc) {
|
||||
var id = acc.getAttribute('data-section-id');
|
||||
var open = sessionStorage.getItem(sectionStateKey(id)) === '1';
|
||||
var open = localStorage.getItem(sectionStateKey(id)) === '1';
|
||||
var def = acc.getAttribute('data-default-open') === 'true';
|
||||
acc.classList.toggle('collapsed', !open && !def);
|
||||
var btn = acc.querySelector('.sub-toggle');
|
||||
@@ -82,7 +82,7 @@
|
||||
var collapsed = acc.classList.contains('collapsed');
|
||||
btn.setAttribute('aria-expanded', String(!collapsed));
|
||||
try {
|
||||
sessionStorage.setItem(sectionStateKey(acc.getAttribute('data-section-id')), collapsed ? '0' : '1');
|
||||
localStorage.setItem(sectionStateKey(acc.getAttribute('data-section-id')), collapsed ? '0' : '1');
|
||||
} catch (e) { /* приватный режим */ }
|
||||
};
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
<script th:src="@{/js/tablekit.js(v='21')}" defer></script>
|
||||
<script th:src="@{/js/bootstrap.bundle.min.js}" defer></script>
|
||||
<script th:src="@{/js/theme-init.js(v='21')}"></script>
|
||||
<script th:src="@{/js/app.js(v='24')}" defer></script>
|
||||
<script th:src="@{/js/ui-state.js(v='24')}" defer></script>
|
||||
<script th:src="@{/js/app.js(v='25')}" defer></script>
|
||||
<script th:src="@{/js/ui-state.js(v='25')}" defer></script>
|
||||
<script th:src="@{/js/md-content.js(v='21')}" defer></script>
|
||||
<script th:src="@{/js/submit-guard.js(v='21')}" defer></script>
|
||||
</head>
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
<head><title th:text="${article.title} + ' — LMS'">Статья</title></head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="learn-layout">
|
||||
<aside class="learn-side">
|
||||
<div class="row g-4 align-items-start">
|
||||
<div class="col-12 col-lg-3 order-lg-1 order-2">
|
||||
<aside class="learn-side offcanvas-lg offcanvas-end" tabindex="-1" id="articleSide" aria-label="Содержание">
|
||||
<div class="offcanvas-header d-lg-none">
|
||||
<span class="offcanvas-title">Содержание</span>
|
||||
<button type="button" class="navbar-close" data-bs-dismiss="offcanvas" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="offcanvas-body p-0">
|
||||
<div class="card card-body">
|
||||
<p class="text-sm text-muted mb-2">Содержание</p>
|
||||
<div th:each="c : ${tree.chapters}">
|
||||
@@ -26,9 +32,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="learn-main">
|
||||
<div class="col-12 col-lg-9 order-1">
|
||||
<nav class="text-sm mb-4 breadcrumb">
|
||||
<a th:href="@{/learn}" class="nav-link">Материалы</a>
|
||||
<span class="nav-sep">/</span>
|
||||
@@ -40,12 +48,18 @@
|
||||
<span th:text="${article.title}">Статья</span>
|
||||
</nav>
|
||||
|
||||
<h1 class="page-title">
|
||||
<div class="d-flex align-items-start justify-content-between gap-2 mb-3">
|
||||
<h1 class="page-title m-0">
|
||||
<span th:text="${article.title}">Статья</span>
|
||||
<span th:if="${!article.published}" class="badge badge-warn ml-2">скрыто от студентов</span>
|
||||
</h1>
|
||||
<button class="btn btn-secondary btn-sm d-lg-none" type="button"
|
||||
data-bs-toggle="offcanvas" data-bs-target="#articleSide" aria-controls="articleSide">
|
||||
Содержание
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4 action-row">
|
||||
<button class="btn btn-secondary btn-sm p-2" type="button" data-action="article-edit" title="Править статью" aria-label="Править статью"><i class="ico bi bi-pencil" aria-hidden="true"></i></button>
|
||||
<form class="inline-form" th:action="@{/admin/content/articles/toggle-published}" method="post">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<span th:if="${!chapter.published}" class="badge badge-warn ml-2">скрыто</span>
|
||||
</h1>
|
||||
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4 action-row">
|
||||
<button class="btn btn-primary" type="button" data-modal="modal-new-section">+ Раздел</button>
|
||||
<button class="btn btn-secondary btn-sm p-2" type="button" data-modal="modal-rename-chapter" title="Переименовать главу" aria-label="Переименовать главу"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
|
||||
<form class="inline-form" th:action="@{/admin/content/chapters/toggle-published}" method="post">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<span th:if="${!section.published}" class="badge badge-warn ml-2">скрыто</span>
|
||||
</h1>
|
||||
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4 action-row">
|
||||
<button class="btn btn-primary" type="button" data-modal="modal-new-article">+ Статья</button>
|
||||
<button class="btn btn-secondary btn-sm p-2" type="button" data-modal="modal-rename-section" title="Переименовать раздел" aria-label="Переименовать раздел"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
|
||||
<form class="inline-form" th:action="@{/admin/content/sections/toggle-published}" method="post">
|
||||
|
||||
Reference in New Issue
Block a user