CMS: Put Documents component in a tiptap node

This commit is contained in:
corsacca 2026-05-07 13:55:55 +01:00
parent b52a1f8ae2
commit 73b6343a43
2 changed files with 91 additions and 8 deletions

View file

@ -215,6 +215,48 @@ function insertVerse() {
}).run()
}
function insertGeneralResources() {
editor.value?.chain().focus().insertContent({
type: 'generalResources',
attrs: { useDocuments: false, layout: 'on-sidebar-page' }
}).run()
}
function toggleGeneralResourcesUseDocuments() {
const e = editor.value
if (!e) return
const current = e.getAttributes('generalResources')
e.chain().focus().updateAttributes('generalResources', {
useDocuments: !current.useDocuments
}).run()
}
function setGeneralResourcesLayout(layout: 'on-sidebar-page' | 'on-page') {
editor.value?.chain().focus().updateAttributes('generalResources', { layout }).run()
}
function generalResourcesAttr<K extends 'useDocuments' | 'layout'>(key: K): unknown {
return editor.value?.getAttributes('generalResources')?.[key]
}
const customNodeMenuItems = computed(() => [
{
label: 'UUPG list',
icon: 'i-lucide-globe',
onSelect: () => insertUupgsList()
},
{
label: 'Verse',
icon: 'i-lucide-book-open',
onSelect: () => insertVerse()
},
{
label: 'Adoption resources',
icon: 'i-lucide-library',
onSelect: () => insertGeneralResources()
}
])
// Clicks on the padding area around the ProseMirror content don't move
// the cursor by default. Forward those clicks to the editor and focus
// at end-of-doc so the click anywhere inside the body area starts editing.
@ -278,8 +320,44 @@ function onBodyClick(e: MouseEvent) {
</UPopover>
<UButton size="xs" variant="ghost" color="neutral" icon="i-lucide-image" aria-label="Image" :loading="uploading" @click="insertImage" />
<UButton size="xs" variant="ghost" color="neutral" icon="i-lucide-youtube" aria-label="YouTube" @click="insertYoutube" />
<UButton size="xs" variant="ghost" :color="isActive('uupgsList') ? 'primary' : 'neutral'" icon="i-lucide-globe" label="UUPG list" aria-label="Insert UUPG list" @click="insertUupgsList" />
<UButton size="xs" variant="ghost" :color="isActive('verse') ? 'primary' : 'neutral'" icon="i-lucide-book-open" label="Verse" aria-label="Insert verse" @click="insertVerse" />
<UDropdownMenu :items="customNodeMenuItems">
<UButton
size="xs"
variant="ghost"
:color="isActive('uupgsList') || isActive('verse') || isActive('generalResources') ? 'primary' : 'neutral'"
icon="i-lucide-plus"
label="Insert"
trailing-icon="i-lucide-chevron-down"
aria-label="Insert custom block"
/>
</UDropdownMenu>
<template v-if="isActive('generalResources')">
<div class="w-px bg-(--ui-border) mx-1" />
<UButton
size="xs"
variant="ghost"
:color="generalResourcesAttr('useDocuments') ? 'primary' : 'neutral'"
:label="generalResourcesAttr('useDocuments') ? 'Documents list' : 'General resources'"
aria-label="Toggle list type"
@click="toggleGeneralResourcesUseDocuments"
/>
<UButton
size="xs"
variant="ghost"
:color="generalResourcesAttr('layout') === 'on-sidebar-page' ? 'primary' : 'neutral'"
label="Sidebar"
aria-label="Layout: on a sidebar page"
@click="setGeneralResourcesLayout('on-sidebar-page')"
/>
<UButton
size="xs"
variant="ghost"
:color="generalResourcesAttr('layout') === 'on-page' ? 'primary' : 'neutral'"
label="Full page"
aria-label="Layout: on a full page"
@click="setGeneralResourcesLayout('on-page')"
/>
</template>
<div class="w-px bg-(--ui-border) mx-1" />
<UButton size="xs" variant="ghost" color="neutral" icon="i-lucide-undo-2" aria-label="Undo" @click="cmd(e => e.chain().focus().undo().run())" />
<UButton size="xs" variant="ghost" color="neutral" icon="i-lucide-redo-2" aria-label="Redo" @click="cmd(e => e.chain().focus().redo().run())" />
@ -315,7 +393,8 @@ function onBodyClick(e: MouseEvent) {
.tiptap-body :deep(iframe) { max-width: 100%; }
.tiptap-body :deep(.ProseMirror:focus) { outline: none; }
.tiptap-body :deep(.doxa-uupgs-list-editor-chip) {
.tiptap-body :deep(.doxa-uupgs-list-editor-chip),
.tiptap-body :deep(.doxa-general-resources-editor-chip) {
display: block;
margin: 0.75rem 0;
padding: 0.75rem 1rem;
@ -326,19 +405,23 @@ function onBodyClick(e: MouseEvent) {
user-select: none;
cursor: grab;
}
.tiptap-body :deep(.doxa-uupgs-list-editor-chip.is-selected) {
.tiptap-body :deep(.doxa-uupgs-list-editor-chip.is-selected),
.tiptap-body :deep(.doxa-general-resources-editor-chip.is-selected) {
outline: 2px solid var(--ui-primary);
outline-offset: 1px;
}
.tiptap-body :deep(.doxa-uupgs-list-editor-chip__header) {
.tiptap-body :deep(.doxa-uupgs-list-editor-chip__header),
.tiptap-body :deep(.doxa-general-resources-editor-chip__header) {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
font-size: 0.875rem;
}
.tiptap-body :deep(.doxa-uupgs-list-editor-chip__icon) { font-size: 1rem; }
.tiptap-body :deep(.doxa-uupgs-list-editor-chip__detail) {
.tiptap-body :deep(.doxa-uupgs-list-editor-chip__icon),
.tiptap-body :deep(.doxa-general-resources-editor-chip__icon) { font-size: 1rem; }
.tiptap-body :deep(.doxa-uupgs-list-editor-chip__detail),
.tiptap-body :deep(.doxa-general-resources-editor-chip__detail) {
margin-top: 0.25rem;
font-size: 0.75rem;
color: var(--ui-text-muted);

View file

@ -63,7 +63,7 @@ export const GeneralResourcesNode = Node.create({
const paintFrom = (n: typeof node) => {
const attrs: Record<string, any> = n.attrs
const parts: string[] = []
parts.push(attrs.useDocuments ? 'documents list' : 'general resources list')
parts.push(attrs.useDocuments ? 'documents list' : 'adoption resources list')
if (attrs.layout && attrs.layout !== 'on-sidebar-page') parts.push(`layout: ${attrs.layout}`)
const detail = parts.join(' · ')