美化Block Editor 选中文字后的工具栏
This commit is contained in:
parent
80a71c6d46
commit
6b8a88ee42
@ -267,7 +267,7 @@ onMounted(() => {
|
||||
codeBlock: false, // Use CodeBlockLowlight instead
|
||||
link: false, // Configure separately
|
||||
dropcursor: false, // Configure separately with custom options
|
||||
heading: { levels: [1, 2, 3] },
|
||||
heading: { levels: [1, 2, 3, 4, 5, 6] },
|
||||
}),
|
||||
|
||||
// ── Additional text formatting ──
|
||||
@ -551,6 +551,15 @@ function exec(name: string) {
|
||||
case 'h3':
|
||||
chain.toggleHeading({ level: 3 }).run()
|
||||
break
|
||||
case 'h4':
|
||||
chain.toggleHeading({ level: 4 }).run()
|
||||
break
|
||||
case 'h5':
|
||||
chain.toggleHeading({ level: 5 }).run()
|
||||
break
|
||||
case 'h6':
|
||||
chain.toggleHeading({ level: 6 }).run()
|
||||
break
|
||||
case 'quote':
|
||||
chain.toggleBlockquote().run()
|
||||
break
|
||||
@ -594,6 +603,20 @@ function exec(name: string) {
|
||||
|
||||
const activeDropdown = ref<string | null>(null)
|
||||
|
||||
const bubbleHeadingOpen = ref(false)
|
||||
const currentHeadingIcon = computed(() => {
|
||||
const ed = editorRef.value
|
||||
if (!ed) return 'format_paragraph'
|
||||
for (let level = 1; level <= 6; level++) {
|
||||
if (ed.isActive('heading', { level })) return `h${level}`
|
||||
}
|
||||
return 'format_paragraph'
|
||||
})
|
||||
|
||||
function toggleBubbleHeading() {
|
||||
bubbleHeadingOpen.value = !bubbleHeadingOpen.value
|
||||
}
|
||||
|
||||
function toggleDropdown(name: string | null) {
|
||||
activeDropdown.value = activeDropdown.value === name ? null : name
|
||||
}
|
||||
@ -884,9 +907,23 @@ function setHighlightColor(color: string) {
|
||||
<button class="be-tb-btn" :class="{ active: isActive('strike') }" :title="t('Strikethrough')" @click="exec('strike')" v-html="icon('strike')"></button>
|
||||
<button class="be-tb-btn" :class="{ active: isActive('code') }" :title="t('Inline Code')" @click="exec('code')" v-html="icon('code')"></button>
|
||||
<span class="be-tb-divider"></span>
|
||||
<button class="be-tb-btn" :class="{ active: isActive('heading', { level: 1 }) }" :title="t('Heading 1')" @click="exec('h1')" v-html="icon('h1')"></button>
|
||||
<button class="be-tb-btn" :class="{ active: isActive('heading', { level: 2 }) }" :title="t('Heading 2')" @click="exec('h2')" v-html="icon('h2')"></button>
|
||||
<button class="be-tb-btn" :class="{ active: isActive('heading', { level: 3 }) }" :title="t('Heading 3')" @click="exec('h3')" v-html="icon('h3')"></button>
|
||||
<div class="be-tb-dropdown be-bubble-dropdown">
|
||||
<button class="be-tb-btn" :class="{ active: bubbleHeadingOpen }" :title="t('Heading')" @click="toggleBubbleHeading" v-html="icon(currentHeadingIcon)"></button>
|
||||
<div v-if="bubbleHeadingOpen" class="be-tb-dropdown-menu">
|
||||
<div
|
||||
v-for="level in headingLevels" :key="level"
|
||||
class="be-tb-dropdown-item"
|
||||
:class="{ 'be-tb-dropdown-item-active': isActive('heading', { level }) }"
|
||||
@mousedown.prevent="setHeading(level)"
|
||||
><span v-html="icon('h' + level)"></span>{{ t('Heading ' + level) }}</div>
|
||||
<div class="be-tb-dropdown-divider"></div>
|
||||
<div
|
||||
class="be-tb-dropdown-item"
|
||||
:class="{ 'be-tb-dropdown-item-active': isActive('paragraph') }"
|
||||
@mousedown.prevent="setParagraph()"
|
||||
><span v-html="icon('format_paragraph')"></span>{{ t('Paragraph') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="be-tb-divider"></span>
|
||||
<button class="be-tb-btn" :class="{ active: isActive('link') }" :title="t('Link')" @click="openLinkPanel" v-html="icon('link')"></button>
|
||||
<button class="be-tb-btn" :class="{ active: isActive('blockquote') }" :title="t('Blockquote')" @click="exec('quote')" v-html="icon('quote')"></button>
|
||||
@ -935,6 +972,9 @@ const ICONS: Record<string, string> = {
|
||||
h1: makeTextIcon('H1'),
|
||||
h2: makeTextIcon('H2'),
|
||||
h3: makeTextIcon('H3'),
|
||||
h4: makeTextIcon('H4'),
|
||||
h5: makeTextIcon('H5'),
|
||||
h6: makeTextIcon('H6'),
|
||||
format_paragraph: '<path d="M6 4v16m0-8h12m0-8v16" stroke-width="2"/>',
|
||||
link: '<path d="m9 15l6-6m-4-3l.463-.536a5 5 0 0 1 7.071 7.072L18 13m-5 5l-.397.534a5.07 5.07 0 0 1-7.127 0a4.97 4.97 0 0 1 0-7.071L6 11"/>',
|
||||
format_size: '<text x="0.5" y="19" font-family="system-ui,-apple-system,sans-serif" font-size="20" font-weight="700" fill="currentColor" stroke="none">A</text><text x="13" y="19" font-family="system-ui,-apple-system,sans-serif" font-size="14" font-weight="600" fill="currentColor" stroke="none">a</text>',
|
||||
@ -1155,14 +1195,23 @@ function icon(name: string): string {
|
||||
align-items: center;
|
||||
gap: 1px;
|
||||
padding: 4px 6px;
|
||||
background: #1e1e1e;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.12);
|
||||
}
|
||||
.blockeditor-bubble-menu .be-tb-btn { color: #6b7280; }
|
||||
.blockeditor-bubble-menu .be-tb-btn:hover { background: #e5e7eb; color: #1f2937; }
|
||||
.blockeditor-bubble-menu .be-tb-btn.active { background: #2383e2; color: #fff; }
|
||||
.blockeditor-bubble-menu .be-tb-divider { background: #d1d5db; }
|
||||
|
||||
/* Bubble menu heading dropdown - appear above trigger */
|
||||
.be-bubble-dropdown { position: relative; }
|
||||
.be-bubble-dropdown .be-tb-dropdown-menu {
|
||||
bottom: 100%;
|
||||
top: auto;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.blockeditor-bubble-menu .be-tb-btn { color: #b3b3b3; }
|
||||
.blockeditor-bubble-menu .be-tb-btn:hover { background: rgba(255,255,255,0.08); color: #fff; }
|
||||
.blockeditor-bubble-menu .be-tb-btn.active { background: rgba(255,255,255,0.12); color: #fff; }
|
||||
.blockeditor-bubble-menu .be-tb-divider { background: rgba(255,255,255,0.1); }
|
||||
|
||||
/* ── Link Panel ── */
|
||||
.blockeditor-link-panel {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user