add rich text editor

Replace the admin rich text textarea with a Tiptap editor while keeping the existing HTML string save contract. Add public-page styles for the editor output.
This commit is contained in:
Yisroel Baum 2026-06-21 21:22:39 +03:00
parent cdc29b795a
commit b4b3927ab1
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
5 changed files with 1735 additions and 13 deletions

View file

@ -2,6 +2,7 @@
import { storeToRefs } from 'pinia'
import { computed, reactive, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import RichTextEditor from '@/components/RichTextEditor.vue'
import SiteHeader from '@/components/SiteHeader.vue'
import { useElementsStore } from '@/stores/elements'
@ -325,15 +326,10 @@ function resetInput(changeEvent: Event): void {
</p>
</section>
<label class="admin-element-page__field">
<span class="admin-element-page__label">Rich Text HTML</span>
<textarea
v-model="form.richText"
class="admin-element-page__textarea admin-element-page__code"
data-cy="admin-element-rich-text"
rows="9"
/>
</label>
<div class="admin-element-page__field">
<span class="admin-element-page__label">Rich Text</span>
<RichTextEditor v-model="form.richText" :disabled="isSaving" />
</div>
<section class="admin-element-page__media-field">
<span class="admin-element-page__label">Short PDF</span>
@ -559,10 +555,6 @@ function resetInput(changeEvent: Event): void {
resize: vertical;
}
.admin-element-page__code {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.admin-element-page__icon-preview {
width: 6rem;
height: 6rem;

View file

@ -352,11 +352,93 @@ function isShortYoutubeHost(hostname: string): boolean {
margin-bottom: 0;
}
.element-page__rich-text :deep(h2),
.element-page__rich-text :deep(h3),
.element-page__rich-text :deep(h4) {
margin: 1.6rem 0 0.65rem;
color: #2c2c2c;
font-family: var(--font-serif);
font-weight: 500;
line-height: 1.25;
}
.element-page__rich-text :deep(h2) {
font-size: 1.75rem;
}
.element-page__rich-text :deep(h3) {
font-size: 1.45rem;
}
.element-page__rich-text :deep(h4) {
font-size: 1.2rem;
}
.element-page__rich-text :deep(ul),
.element-page__rich-text :deep(ol) {
margin: 0 0 1rem;
padding-left: 1.45rem;
}
.element-page__rich-text :deep(li) {
margin: 0.25rem 0;
}
.element-page__rich-text :deep(blockquote) {
margin: 1.35rem 0;
padding: 0.2rem 0 0.2rem 1.1rem;
border-left: 3px solid var(--color-olive);
color: var(--color-text-muted);
}
.element-page__rich-text :deep(strong) {
color: #2c2c2c;
font-weight: 700;
}
.element-page__rich-text :deep(mark) {
padding: 0 0.16em;
background: #fff3a7;
}
.element-page__rich-text :deep(a) {
color: var(--color-slate);
font-weight: 600;
text-decoration: underline;
text-underline-offset: 0.16em;
}
.element-page__rich-text :deep(hr) {
margin: 1.5rem 0;
border: 0;
border-top: 1px solid var(--color-border);
}
.element-page__rich-text :deep(.tableWrapper) {
max-width: 100%;
margin: 1.4rem 0;
overflow-x: auto;
}
.element-page__rich-text :deep(table) {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.element-page__rich-text :deep(th),
.element-page__rich-text :deep(td) {
min-width: 4rem;
padding: 0.55rem 0.65rem;
border: 1px solid var(--color-border);
vertical-align: top;
}
.element-page__rich-text :deep(th) {
background: #f6f3eb;
font-weight: 700;
}
.element-page__youtube {
aspect-ratio: 16 / 9;
margin-top: 1.75rem;