更新
This commit is contained in:
@@ -3,6 +3,7 @@ import { watch } from "vue";
|
||||
import { useEditor, EditorContent } from "@tiptap/vue-3";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
import Placeholder from "@tiptap/extension-placeholder";
|
||||
import { Markdown } from "@tiptap/markdown";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -29,6 +30,7 @@ const editor = useEditor({
|
||||
Placeholder.configure({
|
||||
placeholder: props.placeholder,
|
||||
}),
|
||||
Markdown,
|
||||
],
|
||||
editorProps: {
|
||||
attributes: {
|
||||
@@ -45,7 +47,7 @@ const editor = useEditor({
|
||||
},
|
||||
},
|
||||
onUpdate: ({ editor }) => {
|
||||
emit("update:modelValue", editor.getHTML());
|
||||
emit("update:modelValue", editor.getMarkdown());
|
||||
},
|
||||
});
|
||||
|
||||
@@ -53,8 +55,8 @@ const editor = useEditor({
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (editor.value && val !== editor.value.getHTML()) {
|
||||
editor.value.commands.setContent(val, { emitUpdate: false });
|
||||
if (editor.value && val !== editor.value.getMarkdown()) {
|
||||
editor.value.commands.setContent(val, { contentType: "markdown", emitUpdate: false });
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -62,12 +64,12 @@ watch(
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col flex-1 w-full border border-gray-700 bg-gray-900 shadow-2xl overflow-hidden"
|
||||
class="flex flex-col flex-1 w-full border border-[#e0dbcf] bg-white shadow-2xl overflow-hidden"
|
||||
>
|
||||
<!-- Toolbar -->
|
||||
<div
|
||||
v-if="editor"
|
||||
class="flex flex-wrap items-center gap-0.5 px-3 py-2 border-b border-gray-700 bg-gray-800/80 backdrop-blur"
|
||||
class="flex flex-wrap items-center gap-0.5 px-3 py-2 border-b border-[#e0dbcf] bg-[#f4f1eb]/80 backdrop-blur"
|
||||
>
|
||||
<!-- Headings -->
|
||||
<button
|
||||
@@ -76,140 +78,140 @@ watch(
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-semibold rounded-md transition-colors',
|
||||
editor.isActive('heading', { level })
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleHeading({ level }).run()"
|
||||
>
|
||||
H{{ level }}
|
||||
</button>
|
||||
|
||||
<span class="w-px h-6 bg-gray-600 mx-1" />
|
||||
<span class="w-px h-6 bg-[#d4cfc4] mx-1" />
|
||||
|
||||
<!-- Inline formatting -->
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('bold')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleBold().run()"
|
||||
>
|
||||
<strong>B</strong>
|
||||
<i class="ri-bold"></i>
|
||||
</button>
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors italic',
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('italic')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleItalic().run()"
|
||||
>
|
||||
I
|
||||
<i class="ri-italic"></i>
|
||||
</button>
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors line-through',
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('strike')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleStrike().run()"
|
||||
>
|
||||
S
|
||||
<i class="ri-strikethrough"></i>
|
||||
</button>
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-mono font-medium rounded-md transition-colors',
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('code')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleCode().run()"
|
||||
>
|
||||
</>
|
||||
<i class="ri-code-line"></i>
|
||||
</button>
|
||||
|
||||
<span class="w-px h-6 bg-gray-600 mx-1" />
|
||||
<span class="w-px h-6 bg-[#d4cfc4] mx-1" />
|
||||
|
||||
<!-- Block elements -->
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('blockquote')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleBlockquote().run()"
|
||||
>
|
||||
"Quote"
|
||||
<i class="ri-double-quotes-l"></i>
|
||||
</button>
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-mono font-medium rounded-md transition-colors',
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('codeBlock')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||
>
|
||||
Code
|
||||
<i class="ri-code-box-line"></i>
|
||||
</button>
|
||||
|
||||
<span class="w-px h-6 bg-gray-600 mx-1" />
|
||||
<span class="w-px h-6 bg-[#d4cfc4] mx-1" />
|
||||
|
||||
<!-- Lists -->
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('bulletList')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleBulletList().run()"
|
||||
>
|
||||
• List
|
||||
<i class="ri-list-unordered"></i>
|
||||
</button>
|
||||
<button
|
||||
:class="[
|
||||
'px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors',
|
||||
editor.isActive('orderedList')
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
? 'bg-[#bf6a3b] text-white'
|
||||
: 'text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e]',
|
||||
]"
|
||||
@click="editor.chain().focus().toggleOrderedList().run()"
|
||||
>
|
||||
1. List
|
||||
<i class="ri-list-ordered"></i>
|
||||
</button>
|
||||
|
||||
<span class="w-px h-6 bg-gray-600 mx-1" />
|
||||
<span class="w-px h-6 bg-[#d4cfc4] mx-1" />
|
||||
|
||||
<!-- Horizontal rule -->
|
||||
<button
|
||||
class="px-2.5 py-1.5 text-xs font-medium rounded-md text-gray-300 hover:bg-gray-700 hover:text-white transition-colors"
|
||||
class="px-2.5 py-1.5 text-xs font-medium rounded-md text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e] transition-colors"
|
||||
@click="editor.chain().focus().setHorizontalRule().run()"
|
||||
>
|
||||
──
|
||||
<i class="ri-separator"></i>
|
||||
</button>
|
||||
|
||||
<span class="flex-1" />
|
||||
|
||||
<!-- Undo / Redo -->
|
||||
<button
|
||||
class="px-2.5 py-1.5 text-xs font-medium rounded-md text-gray-300 hover:bg-gray-700 hover:text-white transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
class="px-2.5 py-1.5 text-xs font-medium rounded-md text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e] transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
:disabled="!editor.can().chain().focus().undo().run()"
|
||||
@click="editor.chain().focus().undo().run()"
|
||||
>
|
||||
↶
|
||||
<i class="ri-arrow-go-back-line"></i>
|
||||
</button>
|
||||
<button
|
||||
class="px-2.5 py-1.5 text-xs font-medium rounded-md text-gray-300 hover:bg-gray-700 hover:text-white transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
class="px-2.5 py-1.5 text-xs font-medium rounded-md text-[#5c574e] hover:bg-[#e0dbcf] hover:text-[#38342e] transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
:disabled="!editor.can().chain().focus().redo().run()"
|
||||
@click="editor.chain().focus().redo().run()"
|
||||
>
|
||||
↷
|
||||
<i class="ri-arrow-go-forward-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -221,14 +223,15 @@ watch(
|
||||
<style>
|
||||
/* TipTap / ProseMirror content styling */
|
||||
.tiptap-editor .ProseMirror {
|
||||
color: #f3f4f6;
|
||||
color: #38342e;
|
||||
min-height: 320px;
|
||||
flex: 1;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tiptap-editor .ProseMirror p.is-editor-empty:first-child::before {
|
||||
color: #6b7280;
|
||||
color: #b8b3a8;
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
@@ -241,37 +244,37 @@ watch(
|
||||
font-weight: 700;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: #fff;
|
||||
color: #2d2a26;
|
||||
}
|
||||
.tiptap-editor .ProseMirror h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #fff;
|
||||
color: #2d2a26;
|
||||
}
|
||||
.tiptap-editor .ProseMirror h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #fff;
|
||||
color: #2d2a26;
|
||||
}
|
||||
|
||||
/* Blockquote */
|
||||
.tiptap-editor .ProseMirror blockquote {
|
||||
border-left: 4px solid #6366f1;
|
||||
border-left: 4px solid #bf6a3b;
|
||||
padding-left: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: #9ca3af;
|
||||
color: #8c877d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
.tiptap-editor .ProseMirror code {
|
||||
background-color: #1f2937;
|
||||
color: #f472b6;
|
||||
background-color: #f4f1ea;
|
||||
color: #bf6a3b;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
@@ -280,7 +283,7 @@ watch(
|
||||
|
||||
/* Code block */
|
||||
.tiptap-editor .ProseMirror pre {
|
||||
background-color: #1f2937;
|
||||
background-color: #f4f1ea;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
@@ -289,7 +292,7 @@ watch(
|
||||
}
|
||||
.tiptap-editor .ProseMirror pre code {
|
||||
background-color: transparent;
|
||||
color: #e5e7eb;
|
||||
color: #5c574e;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -313,28 +316,28 @@ watch(
|
||||
|
||||
/* Horizontal rule */
|
||||
.tiptap-editor .ProseMirror hr {
|
||||
border-color: #374151;
|
||||
border-color: #e8e4da;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.tiptap-editor .ProseMirror a {
|
||||
color: #818cf8;
|
||||
color: #bf6a3b;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Bold / Italic / Strike */
|
||||
.tiptap-editor .ProseMirror strong {
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
color: #2d2a26;
|
||||
}
|
||||
.tiptap-editor .ProseMirror em {
|
||||
font-style: italic;
|
||||
}
|
||||
.tiptap-editor .ProseMirror s {
|
||||
text-decoration: line-through;
|
||||
color: #9ca3af;
|
||||
color: #8c877d;
|
||||
}
|
||||
|
||||
/* Paragraph spacing */
|
||||
|
||||
Reference in New Issue
Block a user