Learn: icon formatting toolbar; fix editor shown when hidden
This commit is contained in:
@@ -44,6 +44,9 @@ body {
|
|||||||
a { color: var(--c-text-link); }
|
a { color: var(--c-text-link); }
|
||||||
a:hover { color: var(--c-text-link-hover); }
|
a:hover { color: var(--c-text-link-hover); }
|
||||||
|
|
||||||
|
/* Атрибут hidden должен скрывать элемент даже если у него display из класса */
|
||||||
|
[hidden] { display: none !important; }
|
||||||
|
|
||||||
/* Layout */
|
/* Layout */
|
||||||
.page { display: flex; flex-direction: column; min-height: 100vh; }
|
.page { display: flex; flex-direction: column; min-height: 100vh; }
|
||||||
.page-header { position: sticky; top: 0; z-index: 10; border-bottom: 1px solid var(--c-border); background: var(--c-surface); }
|
.page-header { position: sticky; top: 0; z-index: 10; border-bottom: 1px solid var(--c-border); background: var(--c-surface); }
|
||||||
@@ -387,16 +390,33 @@ th.th-filterable { padding-bottom: 0.35rem; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Панель форматирования markdown и встроенный редактор */
|
/* Панель форматирования markdown и встроенный редактор */
|
||||||
.md-toolbar { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 0.4rem; }
|
.md-toolbar {
|
||||||
.md-btn {
|
display: flex; flex-wrap: wrap; align-items: center; gap: 0.1rem;
|
||||||
font: inherit; font-size: 0.78rem; line-height: 1.4; cursor: pointer;
|
padding: 0.3rem; margin-bottom: 0.5rem;
|
||||||
padding: 0.2rem 0.5rem; border: 1px solid var(--c-border); border-radius: 0.35rem;
|
border: 1px solid var(--c-border); border-radius: 0.5rem; background: var(--c-bg);
|
||||||
background: var(--c-surface); color: var(--c-text);
|
|
||||||
}
|
}
|
||||||
.md-btn:hover { background: var(--c-bg); }
|
.md-sprite { display: none; }
|
||||||
|
.md-btn {
|
||||||
|
display: inline-flex; align-items: center; justify-content: center;
|
||||||
|
width: 2rem; height: 2rem; padding: 0; cursor: pointer;
|
||||||
|
border: 1px solid transparent; border-radius: 0.375rem;
|
||||||
|
background: transparent; color: var(--c-text);
|
||||||
|
}
|
||||||
|
.md-btn:hover { background: var(--c-surface); border-color: var(--c-border); }
|
||||||
.md-btn:active { transform: translateY(1px); }
|
.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;
|
||||||
|
}
|
||||||
|
.md-sep { width: 1px; align-self: stretch; margin: 0.2rem 0.3rem; background: var(--c-border); }
|
||||||
|
.md-editor-head {
|
||||||
|
display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
|
||||||
|
padding: 0.75rem 1.25rem; border-bottom: 1px solid var(--c-border);
|
||||||
|
}
|
||||||
|
.md-editor-title { font-weight: 600; }
|
||||||
.md-editor-body {
|
.md-editor-body {
|
||||||
width: 100%; min-height: 20rem; font-family: "DejaVu Sans Mono", Consolas, monospace;
|
width: 100%; min-height: 22rem; font-family: "DejaVu Sans Mono", Consolas, monospace;
|
||||||
font-size: 0.9rem; line-height: 1.5; white-space: pre; overflow-wrap: normal;
|
font-size: 0.9rem; line-height: 1.5; white-space: pre; overflow-wrap: normal;
|
||||||
}
|
}
|
||||||
.md-figure { margin: 1rem 0; text-align: center; }
|
.md-figure { margin: 1rem 0; text-align: center; }
|
||||||
|
|||||||
@@ -2,20 +2,56 @@
|
|||||||
<html lang="ru" xmlns:th="http://www.thymeleaf.org">
|
<html lang="ru" xmlns:th="http://www.thymeleaf.org">
|
||||||
<body>
|
<body>
|
||||||
<!-- Панель форматирования markdown. target — CSS-селектор textarea -->
|
<!-- Панель форматирования markdown. target — CSS-селектор textarea -->
|
||||||
<div th:fragment="toolbar(target)" class="md-toolbar" th:attr="data-md-target=${target}">
|
<div th:fragment="toolbar(target)" class="md-toolbar" th:attr="data-md-target=${target}"
|
||||||
<button type="button" class="md-btn" data-md="bold" title="Полужирный: **текст**"><b>Полужирный</b></button>
|
role="toolbar" aria-label="Форматирование текста">
|
||||||
<button type="button" class="md-btn" data-md="italic" title="Курсив: *текст*"><i>Курсив</i></button>
|
<button type="button" class="md-btn" data-md="bold" title="Полужирный" aria-label="Полужирный">
|
||||||
<button type="button" class="md-btn" data-md="h2" title="Заголовок раздела">Заголовок</button>
|
<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>
|
||||||
<button type="button" class="md-btn" data-md="ul" title="Маркированный список">Список</button>
|
</button>
|
||||||
<button type="button" class="md-btn" data-md="ol" title="Нумерованный список">Нумерация</button>
|
<button type="button" class="md-btn" data-md="italic" title="Курсив" aria-label="Курсив">
|
||||||
<button type="button" class="md-btn" data-md="quote" title="Цитата">Цитата</button>
|
<svg class="md-ico" viewBox="0 0 24 24"><path d="M11 5h6M7 19h6M15 5l-4 14"/></svg>
|
||||||
<button type="button" class="md-btn" data-md="link" title="Ссылка">Ссылка</button>
|
</button>
|
||||||
<button type="button" class="md-btn" data-md="code" title="Код в строке">Код</button>
|
|
||||||
<button type="button" class="md-btn" data-md="codeblock" title="Блок кода на C">Блок кода</button>
|
<span class="md-sep"></span>
|
||||||
<button type="button" class="md-btn" data-md="table" title="Таблица">Таблица</button>
|
|
||||||
<button type="button" class="md-btn" data-md="figure" title="Картинка с подписью (файл в assets/)">Картинка</button>
|
<button type="button" class="md-btn" data-md="h2" title="Заголовок раздела" aria-label="Заголовок раздела">
|
||||||
<button type="button" class="md-btn" data-md="video" title="Видео (файл в assets/)">Видео</button>
|
<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>
|
||||||
<button type="button" class="md-btn" data-md="spoiler" title="Спойлер: текст скрыт, открывается по клику">Спойлер</button>
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -82,11 +82,19 @@
|
|||||||
|
|
||||||
<div id="article-view" class="card card-body md-body" th:utext="${article.bodyHtml}"></div>
|
<div id="article-view" class="card card-body md-body" th:utext="${article.bodyHtml}"></div>
|
||||||
|
|
||||||
<form id="article-edit" class="card card-body form-stack" hidden
|
<form id="article-edit" class="card" hidden
|
||||||
th:if="${currentUser != null and currentUser.admin}"
|
th:if="${currentUser != null and currentUser.admin}"
|
||||||
th:action="@{/admin/content/articles/{id}/save(id=${article.id})}" method="post">
|
th:action="@{/admin/content/articles/{id}/save(id=${article.id})}" method="post">
|
||||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||||
<input type="hidden" name="back" th:value="${back}">
|
<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 btn-sm" type="button" onclick="cancelArticleEdit()">Отмена</button>
|
||||||
|
<button class="btn btn-primary btn-sm" type="submit">Сохранить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body form-stack">
|
||||||
<div><label class="label" for="article-title">Заголовок</label>
|
<div><label class="label" for="article-title">Заголовок</label>
|
||||||
<input class="input" id="article-title" name="title" th:value="${article.title}" required></div>
|
<input class="input" id="article-title" name="title" th:value="${article.title}" required></div>
|
||||||
<div>
|
<div>
|
||||||
@@ -95,13 +103,10 @@
|
|||||||
<textarea class="input md-editor-body" id="article-body" name="bodyMd" rows="24"
|
<textarea class="input md-editor-body" id="article-body" name="bodyMd" rows="24"
|
||||||
th:text="${article.bodyMd}"></textarea>
|
th:text="${article.bodyMd}"></textarea>
|
||||||
<p class="hint">
|
<p class="hint">
|
||||||
Кнопки вставляют разметку в текст. Картинки и видео берутся из папки <code class="code">assets/</code>
|
Наводите курсор на значки — подсказка объяснит действие. Картинки и видео берутся из папки
|
||||||
архива материалов (имена файлов — латиницей). Заголовок статьи — отдельное поле выше.
|
<code class="code">assets/</code> архива материалов (имена файлов — латиницей).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
|
||||||
<button class="btn btn-primary" type="submit">Сохранить</button>
|
|
||||||
<button class="btn btn-secondary" type="button" onclick="cancelArticleEdit()">Отмена</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user