Icons as SVG assets via CSS masks; bordeaux links without underline; pencil for article edit

This commit is contained in:
2026-10-02 21:02:13 +03:00
parent f12943c462
commit df919507d7
31 changed files with 144 additions and 133 deletions

View File

@@ -20,8 +20,8 @@ body {
--c-surface: #ffffff;
--c-text: #131316;
--c-text-muted: #74747c;
--c-text-link: #131316;
--c-text-link-hover: #7a2333;
--c-text-link: #7a2333;
--c-text-link-hover: #9d2b3c;
--c-border: #e6e6e9;
--c-border-strong: #d4d4da;
--c-accent: #7a2333;
@@ -47,8 +47,8 @@ body {
--c-surface: #17181d;
--c-text: #e8e9ec;
--c-text-muted: #9a9ba4;
--c-text-link: #e8e9ec;
--c-text-link-hover: #d98b98;
--c-text-link: #d98b98;
--c-text-link-hover: #f0b9c2;
--c-border: #26272e;
--c-border-strong: #34353d;
--c-accent: #a64d5f;
@@ -65,8 +65,8 @@ body {
--c-tone-neu: #9a9ba4;
}
a { color: var(--c-text-link); text-decoration: none; }
a:not(.btn):hover { color: var(--c-text-link-hover); }
a { color: var(--c-accent); text-decoration: none; }
a:not(.btn):hover { color: var(--c-accent-hover); }
/* Атрибут hidden должен скрывать элемент даже если у него display из класса */
[hidden] { display: none !important; }
@@ -87,9 +87,9 @@ a:not(.btn):hover { color: var(--c-text-link-hover); }
/* Nav */
.nav { display: flex; align-items: center; gap: 1.125rem; flex: 1 1 auto; flex-wrap: wrap; font-size: 0.84375rem; }
.nav-link { color: var(--c-text-muted); text-decoration: none; font-weight: 500; }
.nav-link:hover { color: var(--c-text); }
.nav-link.active { color: var(--c-accent); }
.nav-link { color: var(--c-accent); text-decoration: none; font-weight: 500; }
.nav-link:hover { color: var(--c-accent-hover); }
.nav-link.active { color: var(--c-accent); font-weight: 700; }
.nav-sep { color: var(--c-text-muted); }
.logo { font-size: 1rem; font-weight: 700; color: var(--c-text); text-decoration: none; letter-spacing: -0.01em; }
.logo b { color: var(--c-accent); }
@@ -139,6 +139,37 @@ p { margin-top: 0; }
.label { display: block; margin-bottom: 0.25rem; font-size: 0.875rem; font-weight: 500; }
.hint { margin-top: 0.25rem; font-size: 0.75rem; color: var(--c-text-muted); }
/* ---- Icons: SVG-файлы из /icons, подключаются CSS-масками (цвет = currentColor) ---- */
.ico {
display: inline-block; flex: 0 0 auto;
background-color: currentColor;
-webkit-mask-position: center; mask-position: center;
-webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
-webkit-mask-size: contain; mask-size: contain;
}
.ico-eye-open { -webkit-mask-image: url('../icons/eye-open.svg'); mask-image: url('../icons/eye-open.svg'); }
.ico-eye-closed { -webkit-mask-image: url('../icons/eye-closed.svg'); mask-image: url('../icons/eye-closed.svg'); }
.ico-pencil { -webkit-mask-image: url('../icons/pencil.svg'); mask-image: url('../icons/pencil.svg'); }
.ico-hand { -webkit-mask-image: url('../icons/hand.svg'); mask-image: url('../icons/hand.svg'); }
.ico-trash { -webkit-mask-image: url('../icons/trash.svg'); mask-image: url('../icons/trash.svg'); }
.ico-menu { -webkit-mask-image: url('../icons/menu.svg'); mask-image: url('../icons/menu.svg'); }
.ico-chevron-down { -webkit-mask-image: url('../icons/chevron-down.svg'); mask-image: url('../icons/chevron-down.svg'); }
.ico-theme { -webkit-mask-image: url('../icons/theme.svg'); mask-image: url('../icons/theme.svg'); }
.ico-md-bold { -webkit-mask-image: url('../icons/md-bold.svg'); mask-image: url('../icons/md-bold.svg'); }
.ico-md-italic { -webkit-mask-image: url('../icons/md-italic.svg'); mask-image: url('../icons/md-italic.svg'); }
.ico-md-h2 { -webkit-mask-image: url('../icons/md-h2.svg'); mask-image: url('../icons/md-h2.svg'); }
.ico-md-ul { -webkit-mask-image: url('../icons/md-ul.svg'); mask-image: url('../icons/md-ul.svg'); }
.ico-md-ol { -webkit-mask-image: url('../icons/md-ol.svg'); mask-image: url('../icons/md-ol.svg'); }
.ico-md-quote { -webkit-mask-image: url('../icons/md-quote.svg'); mask-image: url('../icons/md-quote.svg'); }
.ico-md-link { -webkit-mask-image: url('../icons/md-link.svg'); mask-image: url('../icons/md-link.svg'); }
.ico-md-code { -webkit-mask-image: url('../icons/md-code.svg'); mask-image: url('../icons/md-code.svg'); }
.ico-md-codeblock { -webkit-mask-image: url('../icons/md-codeblock.svg'); mask-image: url('../icons/md-codeblock.svg'); }
.ico-md-table { -webkit-mask-image: url('../icons/md-table.svg'); mask-image: url('../icons/md-table.svg'); }
.ico-md-figure { -webkit-mask-image: url('../icons/md-figure.svg'); mask-image: url('../icons/md-figure.svg'); }
.ico-md-video { -webkit-mask-image: url('../icons/md-video.svg'); mask-image: url('../icons/md-video.svg'); }
.ico-md-spoiler { -webkit-mask-image: url('../icons/md-spoiler.svg'); mask-image: url('../icons/md-spoiler.svg'); }
/* Cards */
.card { border-radius: var(--radius-lg); background: var(--c-surface); border: 1px solid var(--c-border); }
.card-flush { border-radius: var(--radius-lg); background: var(--c-surface); border: 1px solid var(--c-border); padding: 0; overflow: hidden; }
@@ -342,7 +373,7 @@ html.modal-open body {
.md-body pre { background: var(--c-bg); border: 1px solid var(--c-border); border-radius: var(--radius); padding: 0.75rem 1rem; overflow-x: auto; font-size: 0.8125rem; }
.md-body code { font-family: monospace; }
/* Ссылки в учебных материалах — акцентные, с подчёркиванием: их видно в тексте */
.md-body a { color: var(--c-accent); text-decoration: underline; text-underline-offset: 2px; }
.md-body a { color: var(--c-accent); text-decoration: none; }
.md-body a:hover { color: var(--c-accent-hover); }
.md-body details.spoiler { border: 1px solid var(--c-border); border-radius: var(--radius); padding: 0.5rem 0.75rem; margin: 0.5rem 0; }
.md-body details.spoiler summary { cursor: pointer; font-weight: 500; color: var(--c-accent); }
@@ -504,12 +535,10 @@ dialog.modal.modal-lg .modal-body { max-height: 88vh; min-height: 62vh; }
background: none; cursor: pointer; color: var(--c-text-muted);
}
.sub-toggle:hover { color: var(--c-text); }
.sub-arrow {
display: block; 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); }
.sub-arrow .ico { width: 0.75rem; height: 0.75rem; }
.sub-arrow { transform: rotate(0deg); transition: transform 0.2s; }
.sub-acc.collapsed .sub-arrow { transform: rotate(0deg); }
.sub-acc:not(.collapsed) .sub-arrow { transform: rotate(180deg); }
.section-body { padding-bottom: 0.4rem; }
/* ---- Матрица группы и карточки защиты ---- */
@@ -596,11 +625,9 @@ dialog.modal.modal-lg .modal-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%; }
.actions-col .btn { display: block; width: 100%; text-align: center; }
/* Монохромные SVG-иконки в ячейках матрицы (защита, оценка) */
/* Иконки — SVG-файлы из /icons через CSS-маски: цвет наследуется от текста */
.cell-ico {
width: 0.95rem; height: 0.95rem; display: block;
fill: none; stroke: currentColor; stroke-width: 2;
stroke-linecap: round; stroke-linejoin: round;
}
/* tablekit: сортируемые таблицы и поиск */
@@ -683,8 +710,7 @@ th.th-filterable { padding-bottom: 0.35rem; }
.md-btn:active { transform: translateY(1px); }
.md-btn:focus-visible { outline: 2px solid var(--c-text-link); outline-offset: 1px; }
.md-ico {
width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor;
stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
width: 1.15rem; height: 1.15rem;
}
.md-sep { width: 1px; align-self: stretch; margin: 0.2rem 0.3rem; background: var(--c-border); }
.md-editor-head {

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg>

After

Width:  |  Height:  |  Size: 288 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg>

After

Width:  |  Height:  |  Size: 251 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8"/><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/></svg>

After

Width:  |  Height:  |  Size: 421 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 5h5.2a3.4 3.4 0 0 1 0 6.8H7z"/><path d="M7 11.8h6a3.6 3.6 0 0 1 0 7.2H7z"/></svg>

After

Width:  |  Height:  |  Size: 153 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 8l-4 4 4 4M15 8l4 4-4 4"/></svg>

After

Width:  |  Height:  |  Size: 104 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M9.5 10l-2 2 2 2M14.5 10l2 2-2 2"/></svg>

After

Width:  |  Height:  |  Size: 159 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="8.5" cy="10" r="1.5"/><path d="M4 17l4.5-4.5 3 3 3-3L20 17"/></svg>

After

Width:  |  Height:  |  Size: 188 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 6v12M11 6v12M4 12h7"/><text x="14.5" y="18.2" font-size="10" font-weight="700" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">2</text></svg>

After

Width:  |  Height:  |  Size: 235 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11 5h6M7 19h6M15 5l-4 14"/></svg>

After

Width:  |  Height:  |  Size: 103 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.5 13.5a4 4 0 0 1 0-5.66l2.12-2.12a4 4 0 0 1 5.66 5.66l-1.2 1.2"/><path d="M13.5 10.5a4 4 0 0 1 0 5.66l-2.12 2.12a4 4 0 0 1-5.66-5.66l1.2-1.2"/></svg>

After

Width:  |  Height:  |  Size: 222 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11 6h9M11 12h9M11 18h9"/><text x="3" y="8.4" font-size="7.5" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">1</text><text x="3" y="14.4" font-size="7.5" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">2</text><text x="3" y="20.4" font-size="7.5" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">3</text></svg>

After

Width:  |  Height:  |  Size: 445 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 6v12" stroke-width="3"/><path d="M10 8h9M10 12h9M10 16h6"/></svg>

After

Width:  |  Height:  |  Size: 137 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 3l18 18"/><path d="M10.6 6.2A9.7 9.7 0 0 1 12 6c5 0 9 6 9 6a15.4 15.4 0 0 1-3.5 4M6.4 8.1A15.3 15.3 0 0 0 3 12s4 6 9 6a9.6 9.6 0 0 0 3.1-.5"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/></svg>

After

Width:  |  Height:  |  Size: 258 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 9.7h18M9 9.7V19M15 9.7V19M3 14.3h18"/></svg>

After

Width:  |  Height:  |  Size: 165 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 6h11M9 12h11M9 18h11"/><circle cx="4.6" cy="6" r="1.4" fill="currentColor" stroke="none"/><circle cx="4.6" cy="12" r="1.4" fill="currentColor" stroke="none"/><circle cx="4.6" cy="18" r="1.4" fill="currentColor" stroke="none"/></svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M10 9.5l5 2.5-5 2.5z" fill="currentColor" stroke="none"/></svg>

After

Width:  |  Height:  |  Size: 181 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6h16M4 12h16M4 18h16"/></svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>

After

Width:  |  Height:  |  Size: 222 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="5" fill="none" stroke="currentColor" stroke-width="1.6"/><g stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><line x1="12" y1="1.4" x2="12" y2="3.2"/><line x1="12" y1="20.8" x2="12" y2="22.6"/><line x1="1.4" y1="12" x2="3.2" y2="12"/><line x1="20.8" y1="12" x2="22.6" y2="12"/><line x1="4.5" y1="4.5" x2="5.8" y2="5.8"/><line x1="18.2" y1="18.2" x2="19.5" y2="19.5"/><line x1="4.5" y1="19.5" x2="5.8" y2="18.2"/><line x1="18.2" y1="5.8" x2="19.5" y2="4.5"/></g></svg>

After

Width:  |  Height:  |  Size: 732 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -49,7 +49,7 @@
<button class="mini-btn" type="button"
th:attr="data-url=@{/admin/groups/{id}/assign-modal(id=${group.id}, labId=${lab.id})},data-lab-name=|ЛР ${lab.number}|" data-action="assign"
title="Выдача вариантов: уровни, варианты, автокраспределение по группе">
<svg class="cell-ico" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
<span class="cell-ico ico ico-menu" aria-hidden="true"></span>
</button>
<form class="inline-form" th:with="pub=${labPublication[lab.id]}, pc=${pub != null ? pub.publishedCount : 0}, all=${pub != null and pub.allPublished}"
th:action="@{/admin/groups/{id}/publish(id=${group.id})}" method="post">
@@ -61,8 +61,8 @@
th:attr="data-lab=${lab.id},data-count=${pc},data-total=${pub != null ? pub.total : 0},data-all=${all}"
th:classappend="${all} ? 'is-all' : (${pc > 0} ? 'is-partial' : 'is-empty')"
th:title="${pub == null} ? '' : (${all} ? 'Оценки опубликованы всей группе — нажмите, чтобы скрыть у всех' : (${pc > 0} ? 'Опубликовано ' + ${pc} + ' из ' + ${pub.total} + ' — нажмите, чтобы опубликовать всем' : 'Показать оценки и комментарии всей группе'))">
<svg class="eye-open" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg>
<svg class="eye-closed" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg>
<span class="eye-open ico ico-eye-open" aria-hidden="true"></span>
<span class="eye-closed ico ico-eye-closed" aria-hidden="true"></span>
<span class="mini-count js-pub-count" th:classappend="(${all} or ${pc == 0}) ? 'is-hidden' : ''" th:text="${pc}"></span>
</button>
</form>
@@ -344,7 +344,7 @@
<div class="modal-body" id="assign-modal-body"></div>
</dialog>
<script th:src="@{/js/admin-group.js(v='11')}" defer></script>
<script th:src="@{/js/admin-group.js(v='13')}" defer></script>
</div>
</body>
</html>

View File

@@ -33,10 +33,7 @@
<h2 class="section-title m-0">Условие лабораторной</h2>
<button class="mini-btn" type="button" data-action="lab-edit"
title="Редактировать параметры лабораторной" aria-label="Редактировать параметры лабораторной">
<svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>
</svg>
<span class="cell-ico ico ico-pencil" aria-hidden="true"></span>
</button>
</div>
<div class="card card-body md-body mb-6" id="lab-description-view" th:utext="${descriptionHtml}"></div>
@@ -85,10 +82,7 @@
<div class="flex items-center gap-2">
<button class="mini-btn" type="button" th:attr="data-modal=|modal-level-rename-${level.id}|"
title="Переименовать уровень" aria-label="Переименовать уровень">
<svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>
</svg>
<span class="cell-ico ico ico-pencil" aria-hidden="true"></span>
</button>
<form class="inline-form"
th:action="@{/admin/labs/{labId}/levels/{levelId}/delete(labId=${lab.id}, levelId=${level.id})}"
@@ -96,14 +90,7 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить уровень"
aria-label="Удалить уровень">
<svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
<path d="M10 11v6"/>
<path d="M14 11v6"/>
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
</svg>
<span class="cell-ico ico ico-trash" aria-hidden="true"></span>
</button>
</form>
<button class="btn btn-sm" type="button" th:attr="data-modal=|modal-variant-add-${level.id}|">+
@@ -121,10 +108,7 @@
<div class="flex gap-2">
<button class="mini-btn" type="button" th:attr="data-modal=|modal-variant-edit-${v.id}|"
title="Редактировать вариант" aria-label="Редактировать вариант">
<svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>
</svg>
<span class="cell-ico ico ico-pencil" aria-hidden="true"></span>
</button>
<form class="inline-form"
th:action="@{/admin/labs/{labId}/variants/{variantId}/delete(labId=${lab.id}, variantId=${v.id})}"
@@ -132,14 +116,7 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить вариант"
aria-label="Удалить вариант">
<svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
<path d="M10 11v6"/>
<path d="M14 11v6"/>
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
</svg>
<span class="cell-ico ico ico-trash" aria-hidden="true"></span>
</button>
</form>
</div>
@@ -219,7 +196,7 @@
</dialog>
</th:block>
</div>
<script th:src="@{/js/admin-lab.js(v='11')}" defer></script>
<script th:src="@{/js/admin-lab.js(v='13')}" defer></script>
</div>
</body>
</html>

View File

@@ -15,17 +15,17 @@
</noscript>
<link rel="icon" type="image/svg+xml" th:href="@{/favicon.svg(v='2')}">
<!-- v=4: сброс кэша стилей после редизайна; при изменении css поднимать версию -->
<link rel="stylesheet" th:href="@{/css/app.css(v='11')}">
<link id="hljs-light" rel="stylesheet" th:href="@{/css/hljs-github.css(v='11')}">
<link id="hljs-dark" rel="stylesheet" th:href="@{/css/hljs-github-dark.css(v='11')}" disabled>
<script th:src="@{/js/highlight.min.js(v='11')}" defer></script>
<script th:src="@{/js/table-scroll.js(v='11')}" defer></script>
<script th:src="@{/js/tablekit.js(v='11')}" defer></script>
<script th:src="@{/js/theme-init.js(v='11')}"></script>
<script th:src="@{/js/app.js(v='11')}" defer></script>
<script th:src="@{/js/ui-state.js(v='11')}" defer></script>
<script th:src="@{/js/md-content.js(v='11')}" defer></script>
<script th:src="@{/js/submit-guard.js(v='11')}" defer></script>
<link rel="stylesheet" th:href="@{/css/app.css(v='13')}">
<link id="hljs-light" rel="stylesheet" th:href="@{/css/hljs-github.css(v='13')}">
<link id="hljs-dark" rel="stylesheet" th:href="@{/css/hljs-github-dark.css(v='13')}" disabled>
<script th:src="@{/js/highlight.min.js(v='13')}" defer></script>
<script th:src="@{/js/table-scroll.js(v='13')}" defer></script>
<script th:src="@{/js/tablekit.js(v='13')}" defer></script>
<script th:src="@{/js/theme-init.js(v='13')}"></script>
<script th:src="@{/js/app.js(v='13')}" defer></script>
<script th:src="@{/js/ui-state.js(v='13')}" defer></script>
<script th:src="@{/js/md-content.js(v='13')}" defer></script>
<script th:src="@{/js/submit-guard.js(v='13')}" defer></script>
</head>
<body>
<div class="page">
@@ -48,20 +48,7 @@
</th:block>
<button type="button" class="btn btn-secondary theme-btn"
aria-label="Сменить тему" title="Сменить тему">
<svg class="theme-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<circle cx="12" cy="12" r="5" fill="none" stroke="currentColor" stroke-width="1.6"/>
<path d="M12 7 a5 5 0 0 1 0 10 z" fill="currentColor"/>
<g stroke="currentColor" stroke-width="1.6" stroke-linecap="round">
<line x1="12" y1="1.4" x2="12" y2="3.2"/>
<line x1="12" y1="20.8" x2="12" y2="22.6"/>
<line x1="1.4" y1="12" x2="3.2" y2="12"/>
<line x1="20.8" y1="12" x2="22.6" y2="12"/>
<line x1="4.5" y1="4.5" x2="5.8" y2="5.8"/>
<line x1="18.2" y1="18.2" x2="19.5" y2="19.5"/>
<line x1="4.5" y1="19.5" x2="5.8" y2="18.2"/>
<line x1="18.2" y1="5.8" x2="19.5" y2="4.5"/>
</g>
</svg>
<span class="theme-icon ico ico-theme" aria-hidden="true"></span>
</button>
<a th:if="${currentUser == null}" th:href="@{/login}" class="btn">Войти</a>
<form th:if="${currentUser != null}" th:action="@{/logout}" method="post">

View File

@@ -5,52 +5,52 @@
<div th:fragment="toolbar(target)" class="md-toolbar" th:attr="data-md-target=${target}"
role="toolbar" aria-label="Форматирование текста">
<button type="button" class="md-btn" data-md="bold" title="Полужирный" aria-label="Полужирный">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M7 5h5.2a3.4 3.4 0 0 1 0 6.8H7z"/><path d="M7 11.8h6a3.6 3.6 0 0 1 0 7.2H7z"/></svg>
<span class="md-ico ico ico-md-bold" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="italic" title="Курсив" aria-label="Курсив">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M11 5h6M7 19h6M15 5l-4 14"/></svg>
<span class="md-ico ico ico-md-italic" aria-hidden="true"></span>
</button>
<span class="md-sep"></span>
<button type="button" class="md-btn" data-md="h2" title="Заголовок раздела" aria-label="Заголовок раздела">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M4 6v12M11 6v12M4 12h7"/><text x="14.5" y="18.2" font-size="10" font-weight="700" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">2</text></svg>
<span class="md-ico ico ico-md-h2" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="ul" title="Маркированный список" aria-label="Маркированный список">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M9 6h11M9 12h11M9 18h11"/><circle cx="4.6" cy="6" r="1.4" fill="currentColor" stroke="none"/><circle cx="4.6" cy="12" r="1.4" fill="currentColor" stroke="none"/><circle cx="4.6" cy="18" r="1.4" fill="currentColor" stroke="none"/></svg>
<span class="md-ico ico ico-md-ul" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="ol" title="Нумерованный список" aria-label="Нумерованный список">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M11 6h9M11 12h9M11 18h9"/><text x="3" y="8.4" font-size="7.5" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">1</text><text x="3" y="14.4" font-size="7.5" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">2</text><text x="3" y="20.4" font-size="7.5" fill="currentColor" stroke="none" font-family="system-ui, sans-serif">3</text></svg>
<span class="md-ico ico ico-md-ol" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="quote" title="Цитата" aria-label="Цитата">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M5 6v12" stroke-width="3"/><path d="M10 8h9M10 12h9M10 16h6"/></svg>
<span class="md-ico ico ico-md-quote" aria-hidden="true"></span>
</button>
<span class="md-sep"></span>
<button type="button" class="md-btn" data-md="link" title="Ссылка" aria-label="Ссылка">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M10.5 13.5a4 4 0 0 1 0-5.66l2.12-2.12a4 4 0 0 1 5.66 5.66l-1.2 1.2"/><path d="M13.5 10.5a4 4 0 0 1 0 5.66l-2.12 2.12a4 4 0 0 1-5.66-5.66l1.2-1.2"/></svg>
<span class="md-ico ico ico-md-link" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="code" title="Код в строке" aria-label="Код в строке">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M9 8l-4 4 4 4M15 8l4 4-4 4"/></svg>
<span class="md-ico ico ico-md-code" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="codeblock" title="Блок кода" aria-label="Блок кода">
<svg class="md-ico" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M9.5 10l-2 2 2 2M14.5 10l2 2-2 2"/></svg>
<span class="md-ico ico ico-md-codeblock" aria-hidden="true"></span>
</button>
<span class="md-sep"></span>
<button type="button" class="md-btn" data-md="table" title="Таблица" aria-label="Таблица">
<svg class="md-ico" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 9.7h18M9 9.7V19M15 9.7V19M3 14.3h18"/></svg>
<span class="md-ico ico ico-md-table" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="figure" title="Картинка с подписью" aria-label="Картинка с подписью">
<svg class="md-ico" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="8.5" cy="10" r="1.5"/><path d="M4 17l4.5-4.5 3 3 3-3L20 17"/></svg>
<span class="md-ico ico ico-md-figure" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="video" title="Видео" aria-label="Видео">
<svg class="md-ico" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M10 9.5l5 2.5-5 2.5z" fill="currentColor" stroke="none"/></svg>
<span class="md-ico ico ico-md-video" aria-hidden="true"></span>
</button>
<button type="button" class="md-btn" data-md="spoiler" title="Спойлер (скрытый текст)" aria-label="Спойлер">
<svg class="md-ico" viewBox="0 0 24 24"><path d="M3 3l18 18"/><path d="M10.6 6.2A9.7 9.7 0 0 1 12 6c5 0 9 6 9 6a15.4 15.4 0 0 1-3.5 4M6.4 8.1A15.3 15.3 0 0 0 3 12s4 6 9 6a9.6 9.6 0 0 0 3.1-.5"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/></svg>
<span class="md-ico ico ico-md-spoiler" aria-hidden="true"></span>
</button>
</div>
</body>

View File

@@ -52,15 +52,15 @@
<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="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></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="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
@@ -124,7 +124,7 @@
</div>
<script th:src="@{/js/mdeditor.js(v='11')}" defer></script>
<script th:src="@{/js/mdeditor.js(v='13')}" defer></script>
</div>
</body>
</html>

View File

@@ -16,19 +16,19 @@
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
<button class="btn btn-primary" type="button" data-modal="modal-new-section">+ Раздел</button>
<button class="mini-btn" type="button" data-modal="modal-rename-chapter" title="Переименовать главу" aria-label="Переименовать главу"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="mini-btn" type="button" data-modal="modal-rename-chapter" title="Переименовать главу" aria-label="Переименовать главу"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/chapters/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${chapter.id}">
<input type="hidden" name="published" th:value="${!chapter.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<form class="inline-form" th:action="@{/admin/content/chapters/{id}/delete(id=${chapter.id})}" method="post" data-confirm="Удалить главу со всеми разделами и статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${backParent}">
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
@@ -53,27 +53,27 @@
title="Перетащите мышью: изменить порядок разделов">⠿</span>
<button type="button" class="sub-toggle" aria-expanded="false"
title="Свернуть или развернуть раздел">
<span class="sub-arrow" aria-hidden="true"></span>
<span class="sub-arrow" aria-hidden="true"><span class="cell-ico ico ico-chevron-down" aria-hidden="true"></span></span>
</button>
<a class="group-title sort-title m-0"
th:href="@{/learn/{c}/{s}(c=${chapter.slug}, s=${section.slug})}"
th:text="${section.title}">Раздел</a>
<span th:if="${!section.published}" class="badge badge-warn">скрыто</span>
<div th:if="${currentUser != null and currentUser.admin}" class="sort-actions">
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/sections/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${section.id}">
<input type="hidden" name="published" th:value="${!section.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8"/><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/></svg></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><span class="cell-ico ico ico-hand" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post" data-confirm="Удалить раздел вместе со статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
</div>
@@ -96,8 +96,8 @@
<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="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
</div>
</li>
@@ -183,7 +183,7 @@
</div>
</dialog>
<script th:src="@{/js/content-sort.js(v='11')}" defer></script>
<script th:src="@{/js/content-sort.js(v='13')}" defer></script>
</div>
</body>
</html>

View File

@@ -32,13 +32,13 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${chapter.id}">
<input type="hidden" name="published" th:value="${!chapter.published}">
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-chapter-${chapter.id}|" title="Переименовать главу" aria-label="Переименовать главу"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-chapter-${chapter.id}|" title="Переименовать главу" aria-label="Переименовать главу"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/chapters/{id}/delete(id=${chapter.id})}" method="post" data-confirm="Удалить главу со всеми разделами и статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
</div>
@@ -54,7 +54,7 @@
title="Перетащите мышью: изменить порядок или перенести раздел">⠿</span>
<button type="button" class="sub-toggle" aria-expanded="false"
title="Свернуть или развернуть раздел">
<span class="sub-arrow" aria-hidden="true"></span>
<span class="sub-arrow" aria-hidden="true"><span class="cell-ico ico ico-chevron-down" aria-hidden="true"></span></span>
</button>
<a class="group-title sort-title m-0"
th:href="@{/learn/{c}/{s}(c=${chapter.slug}, s=${section.slug})}"
@@ -65,14 +65,14 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${section.id}">
<input type="hidden" name="published" th:value="${!section.published}">
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи в другой раздел" aria-label="Перенести статьи в другой раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8"/><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/></svg></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи в другой раздел" aria-label="Перенести статьи в другой раздел"><span class="cell-ico ico ico-hand" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post" data-confirm="Удалить раздел со всеми статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить раздел со всеми статьями" aria-label="Удалить раздел со всеми статьями"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить раздел со всеми статьями" aria-label="Удалить раздел со всеми статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
</div>
@@ -92,14 +92,14 @@
<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}">
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<a class="btn btn-secondary"
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}">Править</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}">
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
</li>
@@ -232,8 +232,8 @@
</div>
</dialog>
<script th:src="@{/js/mdeditor.js(v='11')}" defer></script>
<script th:src="@{/js/content-sort.js(v='11')}" defer></script>
<script th:src="@{/js/mdeditor.js(v='13')}" defer></script>
<script th:src="@{/js/content-sort.js(v='13')}" defer></script>
</div>
</body>
</html>

View File

@@ -18,20 +18,20 @@
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
<button class="btn btn-primary" type="button" data-modal="modal-new-article">+ Статья</button>
<button class="mini-btn" type="button" data-modal="modal-rename-section" title="Переименовать раздел" aria-label="Переименовать раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="mini-btn" type="button" data-modal="modal-rename-section" title="Переименовать раздел" aria-label="Переименовать раздел"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/sections/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${section.id}">
<input type="hidden" name="published" th:value="${!section.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<button class="mini-btn" type="button" data-modal="modal-move-section" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8"/><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/></svg></button>
<button class="mini-btn" type="button" data-modal="modal-move-section" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><span class="cell-ico ico ico-hand" aria-hidden="true"></span></button>
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post" data-confirm="Удалить раздел вместе со статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${backParent}">
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
</form>
</div>
@@ -63,8 +63,8 @@
<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="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><svg class="cell-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
</form>
<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}">
@@ -143,7 +143,7 @@
</div>
</dialog>
<script th:src="@{/js/content-sort.js(v='11')}" defer></script>
<script th:src="@{/js/content-sort.js(v='13')}" defer></script>
</div>
</body>
</html>

View File

@@ -107,7 +107,7 @@
<p class="text-sm text-muted">Ожидайте преподавателя с проверкой.</p>
</div>
</div>
<script th:src="@{/js/defense-timer.js(v='11')}" defer></script>
<script th:src="@{/js/defense-timer.js(v='13')}" defer></script>
</div>
</body>
</html>