Files
LMS/web/src/main/resources/templates/learn/article.html
Vladislav Kopnin 3827955909 fix: opaque sticky matrix column and square publish marker
Use solid row hover colors so the sticky student column does not show underlying cells while scrolling. Replace the circular checkbox marker with a square one and bump asset version to 36.
2026-10-20 22:03:50 +03:00

145 lines
9.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru" xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/base :: layout(~{::title}, ~{::#content})}">
<head><title th:text="${article.title} + ' — LMS'">Статья</title></head>
<body>
<div id="content">
<div class="row g-4">
<div class="col-12 col-lg-3 order-lg-1 order-2">
<aside class="learn-side offcanvas 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}">
<div class="side-chapter">
<a th:href="@{/learn/{c}(c=${c.slug})}" th:text="${c.title}">Глава</a>
<span th:if="${!c.published}" class="badge badge-warn">скрыто</span>
</div>
<div th:each="s : ${c.sections}" class="side-section">
<a th:href="@{/learn/{c}/{s}(c=${c.slug}, s=${s.slug})}" th:text="${s.title}">Раздел</a>
<span th:if="${!s.published}" class="badge badge-warn">скрыто</span>
<ul class="side-articles">
<li th:each="a : ${s.articles}">
<a th:href="@{/learn/{c}/{s}/{a2}(c=${c.slug}, s=${s.slug}, a2=${a.slug})}"
th:text="${a.title}"
th:classappend="${a.slug == article.slug and s.slug == article.sectionSlug and c.slug == article.chapterSlug} ? 'side-current'">Статья</a>
<span th:if="${!a.published}" class="badge badge-warn">скрыто</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
</div>
<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>
<a th:href="@{/learn/{c}(c=${article.chapterSlug})}" class="nav-link" th:text="${article.chapterTitle}">Глава</a>
<span class="nav-sep">/</span>
<a th:href="@{/learn/{c}/{s}(c=${article.chapterSlug}, s=${article.sectionSlug})}" class="nav-link"
th:text="${article.sectionTitle}">Раздел</a>
<span class="nav-sep">/</span>
<span th:text="${article.title}">Статья</span>
</nav>
<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 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}">
<input type="hidden" name="id" th:value="${article.id}">
<input type="hidden" name="published" th:value="${!article.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="btn btn-secondary btn-sm p-2" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="btn btn-secondary btn-sm p-2" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<a class="btn btn-secondary"
th:href="@{/learn/{c}(c=${article.chapterSlug})}">К главе</a>
<form class="inline-form" th:action="@{/admin/content/articles/{id}/delete(id=${article.id})}" method="post" data-confirm="Удалить статью?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${backParent}">
<button class="btn btn-danger btn-sm p-2" type="submit" title="Удалить статью" aria-label="Удалить статью"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
<nav class="article-nav mb-4" th:if="${prevArticle != null or nextArticle != null}">
<a th:if="${prevArticle != null}" class="btn btn-secondary"
th:href="@{/learn/{c}/{s}/{a}(c=${prevArticle.chapterSlug}, s=${prevArticle.sectionSlug}, a=${prevArticle.slug})}"
th:title="|${prevArticle.chapterTitle} / ${prevArticle.sectionTitle}|">
← <span th:text="${prevArticle.title}">Предыдущая</span>
</a>
<span th:if="${prevArticle == null}"></span>
<a th:if="${nextArticle != null}" class="btn btn-secondary ml-auto"
th:href="@{/learn/{c}/{s}/{a}(c=${nextArticle.chapterSlug}, s=${nextArticle.sectionSlug}, a=${nextArticle.slug})}"
th:title="|${nextArticle.chapterTitle} / ${nextArticle.sectionTitle}|">
<span th:text="${nextArticle.title}">Следующая</span> →
</a>
</nav>
<div id="article-view" class="card card-body md-body" th:utext="${article.bodyHtml}"></div>
<form id="article-edit" class="card" hidden
th:if="${currentUser != null and currentUser.admin}"
th:action="@{/admin/content/articles/{id}/save(id=${article.id})}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${back}">
<div class="md-editor-head">
<span class="md-editor-title">Редактирование статьи</span>
<div class="flex gap-2">
<button class="btn btn-secondary" type="button" data-action="article-edit-cancel">Отмена</button>
<button class="btn btn-primary" type="submit">Сохранить</button>
</div>
</div>
<div class="card-body form-stack">
<div><label class="label" for="article-title">Заголовок</label>
<input class="input" id="article-title" name="title" th:value="${article.title}" required></div>
<div>
<label class="label" for="article-body">Текст статьи (markdown)</label>
<div th:replace="~{fragments/md-toolbar :: toolbar('#article-body')}"></div>
<textarea class="input md-editor-body" id="article-body" name="bodyMd" rows="24"
th:text="${article.bodyMd}"></textarea>
<p class="hint">
Наводите курсор на значки — подсказка объяснит действие. Картинки и видео берутся из папки
<code class="code">assets/</code> архива материалов (имена файлов — латиницей).
</p>
</div>
</div>
</form>
<nav class="article-nav mt-6" th:if="${prevArticle != null or nextArticle != null}">
<a th:if="${prevArticle != null}" class="btn btn-secondary"
th:href="@{/learn/{c}/{s}/{a}(c=${prevArticle.chapterSlug}, s=${prevArticle.sectionSlug}, a=${prevArticle.slug})}">
← <span th:text="${prevArticle.title}">Предыдущая</span>
</a>
<span th:if="${prevArticle == null}"></span>
<a th:if="${nextArticle != null}" class="btn btn-primary ml-auto"
th:href="@{/learn/{c}/{s}/{a}(c=${nextArticle.chapterSlug}, s=${nextArticle.sectionSlug}, a=${nextArticle.slug})}">
<span th:text="${nextArticle.title}">Следующая</span> →
</a>
</nav>
</div>
</div>
<script th:src="@{/js/mdeditor.js(v='36')}" defer></script>
</div>
</body>
</html>