editor expansion
This commit is contained in:
26
package-lock.json
generated
26
package-lock.json
generated
@@ -5,6 +5,8 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tiptap/extension-text-align": "^2.0.3",
|
||||||
|
"@tiptap/extension-underline": "^2.0.3",
|
||||||
"@tiptap/pm": "^2.0.3",
|
"@tiptap/pm": "^2.0.3",
|
||||||
"@tiptap/starter-kit": "^2.0.3",
|
"@tiptap/starter-kit": "^2.0.3",
|
||||||
"@tiptap/vue-3": "^2.0.3",
|
"@tiptap/vue-3": "^2.0.3",
|
||||||
@@ -1632,6 +1634,30 @@
|
|||||||
"@tiptap/core": "^2.0.0"
|
"@tiptap/core": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tiptap/extension-text-align": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-VlLgqncKdjMjVjbU60/ALYhFs0wUdjAyvjDXnH1OoM/HuzbILvufPMYz4DUieJIWVJOYUKHQgg4XwBWceAM2Tw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tiptap/core": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tiptap/extension-underline": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-oMYa7qib/5wJjpUp79GZEe+E/iyf1oZBsgiG26IspEtVTHZmpn3+Ktud7l43y/hpTeEzFTKOF1/uVbayHtSERg==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tiptap/core": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tiptap/pm": {
|
"node_modules/@tiptap/pm": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.0.3.tgz",
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
"vitest": "^0.32.0"
|
"vitest": "^0.32.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tiptap/extension-text-align": "^2.0.3",
|
||||||
|
"@tiptap/extension-underline": "^2.0.3",
|
||||||
"@tiptap/pm": "^2.0.3",
|
"@tiptap/pm": "^2.0.3",
|
||||||
"@tiptap/starter-kit": "^2.0.3",
|
"@tiptap/starter-kit": "^2.0.3",
|
||||||
"@tiptap/vue-3": "^2.0.3",
|
"@tiptap/vue-3": "^2.0.3",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
'px-4',
|
'px-4',
|
||||||
'pt-5',
|
'pt-5',
|
||||||
'flex-1',
|
'flex-1',
|
||||||
|
'bg-white',
|
||||||
{ 'bg-gray-1': disabled },
|
{ 'bg-gray-1': disabled },
|
||||||
]"
|
]"
|
||||||
v-bind="{
|
v-bind="{
|
||||||
@@ -121,21 +122,21 @@ const label = ref(
|
|||||||
? props.label
|
? props.label
|
||||||
: typeof props.control == "string"
|
: typeof props.control == "string"
|
||||||
? toTitleCase(props.control)
|
? toTitleCase(props.control)
|
||||||
: ""
|
: "",
|
||||||
);
|
);
|
||||||
const value = ref(
|
const value = ref(
|
||||||
props.modelValue != undefined
|
props.modelValue != undefined
|
||||||
? props.modelValue
|
? props.modelValue
|
||||||
: control != null
|
: control != null
|
||||||
? control.value
|
? control.value
|
||||||
: ""
|
: "",
|
||||||
);
|
);
|
||||||
const id = ref(
|
const id = ref(
|
||||||
props.id != undefined
|
props.id != undefined
|
||||||
? props.id
|
? props.id
|
||||||
: typeof props.control == "string" && props.control.length > 0
|
: typeof props.control == "string" && props.control.length > 0
|
||||||
? props.control
|
? props.control
|
||||||
: generateRandomElementId()
|
: generateRandomElementId(),
|
||||||
);
|
);
|
||||||
const active = ref(value.value?.toString().length ?? 0 > 0);
|
const active = ref(value.value?.toString().length ?? 0 > 0);
|
||||||
const focused = ref(false);
|
const focused = ref(false);
|
||||||
@@ -145,7 +146,7 @@ watch(
|
|||||||
() => value.value,
|
() => value.value,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
active.value = newValue.toString().length > 0 || focused.value == true;
|
active.value = newValue.toString().length > 0 || focused.value == true;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (props.modelValue != undefined) {
|
if (props.modelValue != undefined) {
|
||||||
@@ -153,7 +154,7 @@ if (props.modelValue != undefined) {
|
|||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
value.value = newValue;
|
value.value = newValue;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +162,7 @@ watch(
|
|||||||
() => props.disabled,
|
() => props.disabled,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
disabled.value = newValue;
|
disabled.value = newValue;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof control === "object" && control !== null) {
|
if (typeof control === "object" && control !== null) {
|
||||||
@@ -170,7 +171,7 @@ if (typeof control === "object" && control !== null) {
|
|||||||
(newValue) => {
|
(newValue) => {
|
||||||
value.value = newValue;
|
value.value = newValue;
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,156 +1,387 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sm-editor">
|
<div class="sm-html">
|
||||||
<div
|
<div
|
||||||
v-if="editor"
|
v-if="editor"
|
||||||
class="flex bg-white p-1 border-t border-x border-gray rounded-t-2">
|
class="flex bg-white border-t border-x border-gray rounded-t-2">
|
||||||
<button
|
<button
|
||||||
@click.prevent="editor.chain().focus().toggleBold().run()"
|
@click.prevent="editor.chain().focus().toggleInfo().run()"
|
||||||
:disabled="!editor.can().chain().focus().toggleBold().run()"
|
|
||||||
:class="[
|
:class="[
|
||||||
'flex',
|
'flex',
|
||||||
editor.isActive('bold')
|
'flex-items-center',
|
||||||
? ['bg-sky-6', 'text-white']
|
|
||||||
: ['bg-white', 'text-gray-6'],
|
|
||||||
'p-1',
|
'p-1',
|
||||||
'hover-bg-gray-3',
|
'hover-bg-gray-3',
|
||||||
|
editor.isActive('info')
|
||||||
|
? ['bg-sky-6', 'text-white']
|
||||||
|
: ['bg-white', 'text-gray-6'],
|
||||||
]">
|
]">
|
||||||
|
III
|
||||||
|
</button>
|
||||||
|
<div class="flex px-1 border-r border-gray relative">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-5 w-5"
|
viewBox="0 -960 960 960"
|
||||||
viewBox="0 0 24 24">
|
class="absolute right-1 top-1.5 h-6 pointer-events-none">
|
||||||
<path
|
<path
|
||||||
d="M13.5,15.5H10V12.5H13.5A1.5,1.5 0 0,1 15,14A1.5,1.5 0 0,1 13.5,15.5M10,6.5H13A1.5,1.5 0 0,1 14.5,8A1.5,1.5 0 0,1 13,9.5H10M15.6,10.79C16.57,10.11 17.25,9 17.25,8C17.25,5.74 15.5,4 13.25,4H7V18H14.04C16.14,18 17.75,16.3 17.75,14.21C17.75,12.69 16.89,11.39 15.6,10.79Z"
|
d="M480-360 280-559h400L480-360Z"
|
||||||
fill="currentColor" />
|
fill="currentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
<select
|
||||||
<button
|
class="appearance-none pl-3 pr-7 text-xs outline-none select-none bg-white"
|
||||||
@click.prevent="editor.chain().focus().toggleItalic().run()"
|
@change="updateNode">
|
||||||
:disabled="!editor.can().chain().focus().toggleItalic().run()"
|
<option
|
||||||
:class="{ 'is-active': editor.isActive('italic') }">
|
value="paragraph"
|
||||||
italic
|
:selected="editor.isActive('paragraph')">
|
||||||
</button>
|
Paragraph
|
||||||
<button
|
</option>
|
||||||
@click.prevent="editor.chain().focus().toggleStrike().run()"
|
<option
|
||||||
:disabled="!editor.can().chain().focus().toggleStrike().run()"
|
value="h1"
|
||||||
:class="{ 'is-active': editor.isActive('strike') }">
|
:selected="editor.isActive('heading', { level: 1 })">
|
||||||
strike
|
Heading 1
|
||||||
</button>
|
</option>
|
||||||
<button
|
<option
|
||||||
@click.prevent="editor.chain().focus().toggleCode().run()"
|
value="h2"
|
||||||
:disabled="!editor.can().chain().focus().toggleCode().run()"
|
:selected="editor.isActive('heading', { level: 2 })">
|
||||||
:class="{ 'is-active': editor.isActive('code') }">
|
Heading 2
|
||||||
code
|
</option>
|
||||||
</button>
|
<option
|
||||||
<button
|
value="h3"
|
||||||
@click.prevent="editor.chain().focus().unsetAllMarks().run()">
|
:selected="editor.isActive('heading', { level: 3 })">
|
||||||
clear marks
|
Heading 3
|
||||||
</button>
|
</option>
|
||||||
<button @click.prevent="editor.chain().focus().clearNodes().run()">
|
<option
|
||||||
clear nodes
|
value="h4"
|
||||||
</button>
|
:selected="editor.isActive('heading', { level: 4 })">
|
||||||
<button
|
Heading 4
|
||||||
@click.prevent="editor.chain().focus().setParagraph().run()"
|
</option>
|
||||||
:class="{ 'is-active': editor.isActive('paragraph') }">
|
<option
|
||||||
paragraph
|
value="h5"
|
||||||
</button>
|
:selected="editor.isActive('heading', { level: 5 })">
|
||||||
<button
|
Heading 5
|
||||||
@click.prevent="
|
</option>
|
||||||
editor.chain().focus().toggleHeading({ level: 1 }).run()
|
<option
|
||||||
"
|
value="h6"
|
||||||
:class="{
|
:selected="editor.isActive('heading', { level: 6 })">
|
||||||
'is-active': editor.isActive('heading', { level: 1 }),
|
Heading 6
|
||||||
}">
|
</option>
|
||||||
h1
|
</select>
|
||||||
</button>
|
</div>
|
||||||
<button
|
<div class="flex p-1 border-r border-gray">
|
||||||
@click.prevent="
|
<button
|
||||||
editor.chain().focus().toggleHeading({ level: 2 }).run()
|
@click.prevent="editor.chain().focus().toggleBold().run()"
|
||||||
"
|
:disabled="!editor.can().chain().focus().toggleBold().run()"
|
||||||
:class="{
|
title="bold"
|
||||||
'is-active': editor.isActive('heading', { level: 2 }),
|
:class="[
|
||||||
}">
|
'flex',
|
||||||
h2
|
'flex-items-center',
|
||||||
</button>
|
'p-1',
|
||||||
<button
|
'hover-bg-gray-3',
|
||||||
@click.prevent="
|
editor.isActive('bold')
|
||||||
editor.chain().focus().toggleHeading({ level: 3 }).run()
|
? ['bg-sky-6', 'text-white']
|
||||||
"
|
: ['bg-white', 'text-gray-6'],
|
||||||
:class="{
|
]">
|
||||||
'is-active': editor.isActive('heading', { level: 3 }),
|
<svg
|
||||||
}">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
h3
|
class="h-5 w-5"
|
||||||
</button>
|
viewBox="0 0 24 24">
|
||||||
<button
|
<path
|
||||||
@click.prevent="
|
d="M13.5,15.5H10V12.5H13.5A1.5,1.5 0 0,1 15,14A1.5,1.5 0 0,1 13.5,15.5M10,6.5H13A1.5,1.5 0 0,1 14.5,8A1.5,1.5 0 0,1 13,9.5H10M15.6,10.79C16.57,10.11 17.25,9 17.25,8C17.25,5.74 15.5,4 13.25,4H7V18H14.04C16.14,18 17.75,16.3 17.75,14.21C17.75,12.69 16.89,11.39 15.6,10.79Z"
|
||||||
editor.chain().focus().toggleHeading({ level: 4 }).run()
|
fill="currentColor" />
|
||||||
"
|
</svg>
|
||||||
:class="{
|
</button>
|
||||||
'is-active': editor.isActive('heading', { level: 4 }),
|
<button
|
||||||
}">
|
@click.prevent="editor.chain().focus().toggleItalic().run()"
|
||||||
h4
|
:disabled="
|
||||||
</button>
|
!editor.can().chain().focus().toggleItalic().run()
|
||||||
<button
|
"
|
||||||
@click.prevent="
|
title="italic"
|
||||||
editor.chain().focus().toggleHeading({ level: 5 }).run()
|
:class="[
|
||||||
"
|
'flex',
|
||||||
:class="{
|
'flex-items-center',
|
||||||
'is-active': editor.isActive('heading', { level: 5 }),
|
'p-1',
|
||||||
}">
|
'hover-bg-gray-3',
|
||||||
h5
|
editor.isActive('italic')
|
||||||
</button>
|
? ['bg-sky-6', 'text-white']
|
||||||
<button
|
: ['bg-white', 'text-gray-6'],
|
||||||
@click.prevent="
|
]">
|
||||||
editor.chain().focus().toggleHeading({ level: 6 }).run()
|
<svg
|
||||||
"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
:class="{
|
class="h-5 w-5"
|
||||||
'is-active': editor.isActive('heading', { level: 6 }),
|
viewBox="0 0 24 24">
|
||||||
}">
|
<path
|
||||||
h6
|
d="M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z"
|
||||||
</button>
|
fill="currentColor" />
|
||||||
<button
|
</svg>
|
||||||
@click.prevent="editor.chain().focus().toggleBulletList().run()"
|
</button>
|
||||||
:class="{ 'is-active': editor.isActive('bulletList') }">
|
<button
|
||||||
bullet list
|
@click.prevent="
|
||||||
</button>
|
editor.chain().focus().toggleUnderline().run()
|
||||||
<button
|
"
|
||||||
@click.prevent="
|
:disabled="
|
||||||
editor.chain().focus().toggleOrderedList().run()
|
!editor.can().chain().focus().toggleUnderline().run()
|
||||||
"
|
"
|
||||||
:class="{ 'is-active': editor.isActive('orderedList') }">
|
title="underline"
|
||||||
ordered list
|
:class="[
|
||||||
</button>
|
'flex',
|
||||||
<button
|
'flex-items-center',
|
||||||
@click.prevent="editor.chain().focus().toggleCodeBlock().run()"
|
'p-1',
|
||||||
:class="{ 'is-active': editor.isActive('codeBlock') }">
|
'hover-bg-gray-3',
|
||||||
code block
|
editor.isActive('underline')
|
||||||
</button>
|
? ['bg-sky-6', 'text-white']
|
||||||
<button
|
: ['bg-white', 'text-gray-6'],
|
||||||
@click.prevent="editor.chain().focus().toggleBlockquote().run()"
|
]">
|
||||||
:class="{ 'is-active': editor.isActive('blockquote') }">
|
<svg
|
||||||
blockquote
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</button>
|
class="h-4 w-5"
|
||||||
<button
|
viewBox="0 0 24 24">
|
||||||
@click.prevent="
|
<title>format-underline</title>
|
||||||
editor.chain().focus().setHorizontalRule().run()
|
<path
|
||||||
">
|
d="M5,21H19V19H5V21M12,17A6,6 0 0,0 18,11V3H15.5V11A3.5,3.5 0 0,1 12,14.5A3.5,3.5 0 0,1 8.5,11V3H6V11A6,6 0 0,0 12,17Z"
|
||||||
horizontal rule
|
fill="currentColor" />
|
||||||
</button>
|
</svg>
|
||||||
<button
|
</button>
|
||||||
@click.prevent="editor.chain().focus().setHardBreak().run()">
|
<button
|
||||||
hard break
|
@click.prevent="editor.chain().focus().toggleStrike().run()"
|
||||||
</button>
|
:disabled="
|
||||||
<button
|
!editor.can().chain().focus().toggleStrike().run()
|
||||||
@click.prevent="editor.chain().focus().undo().run()"
|
"
|
||||||
:disabled="!editor.can().chain().focus().undo().run()">
|
title="strike"
|
||||||
undo
|
:class="[
|
||||||
</button>
|
'flex',
|
||||||
<button
|
'flex-items-center',
|
||||||
@click.prevent="editor.chain().focus().redo().run()"
|
'p-1',
|
||||||
:disabled="!editor.can().chain().focus().redo().run()">
|
'hover-bg-gray-3',
|
||||||
redo
|
editor.isActive('strike')
|
||||||
</button>
|
? ['bg-sky-6', 'text-white']
|
||||||
|
: ['bg-white', 'text-gray-6'],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>format-strikethrough-variant</title>
|
||||||
|
<path
|
||||||
|
d="M23,12V14H18.61C19.61,16.14 19.56,22 12.38,22C4.05,22.05 4.37,15.5 4.37,15.5L8.34,15.55C8.37,18.92 11.5,18.92 12.12,18.88C12.76,18.83 15.15,18.84 15.34,16.5C15.42,15.41 14.32,14.58 13.12,14H1V12H23M19.41,7.89L15.43,7.86C15.43,7.86 15.6,5.09 12.15,5.08C8.7,5.06 9,7.28 9,7.56C9.04,7.84 9.34,9.22 12,9.88H5.71C5.71,9.88 2.22,3.15 10.74,2C19.45,0.8 19.43,7.91 19.41,7.89Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex p-1 border-r border-gray">
|
||||||
|
<button
|
||||||
|
@click.prevent="
|
||||||
|
editor.chain().focus().toggleBulletList().run()
|
||||||
|
"
|
||||||
|
title="bullet list"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
editor.isActive('bulletList')
|
||||||
|
? ['bg-sky-6', 'text-white']
|
||||||
|
: ['bg-white', 'text-gray-6'],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M7,5H21V7H7V5M7,13V11H21V13H7M4,4.5A1.5,1.5 0 0,1 5.5,6A1.5,1.5 0 0,1 4,7.5A1.5,1.5 0 0,1 2.5,6A1.5,1.5 0 0,1 4,4.5M4,10.5A1.5,1.5 0 0,1 5.5,12A1.5,1.5 0 0,1 4,13.5A1.5,1.5 0 0,1 2.5,12A1.5,1.5 0 0,1 4,10.5M7,19V17H21V19H7M4,16.5A1.5,1.5 0 0,1 5.5,18A1.5,1.5 0 0,1 4,19.5A1.5,1.5 0 0,1 2.5,18A1.5,1.5 0 0,1 4,16.5Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click.prevent="
|
||||||
|
editor.chain().focus().toggleOrderedList().run()
|
||||||
|
"
|
||||||
|
title="ordered list"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
editor.isActive('orderedList')
|
||||||
|
? ['bg-sky-6', 'text-white']
|
||||||
|
: ['bg-white', 'text-gray-6'],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>format-list-numbered</title>
|
||||||
|
<path
|
||||||
|
d="M7,13V11H21V13H7M7,19V17H21V19H7M7,7V5H21V7H7M3,8V5H2V4H4V8H3M2,17V16H5V20H2V19H4V18.5H3V17.5H4V17H2M4.25,10A0.75,0.75 0 0,1 5,10.75C5,10.95 4.92,11.14 4.79,11.27L3.12,13H5V14H2V13.08L4,11H2V10H4.25Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex p-1 border-r border-gray">
|
||||||
|
<button
|
||||||
|
@click.prevent="
|
||||||
|
editor.chain().focus().toggleCodeBlock().run()
|
||||||
|
"
|
||||||
|
title="code block"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
editor.isActive('codeBlock')
|
||||||
|
? ['bg-sky-6', 'text-white']
|
||||||
|
: ['bg-white', 'text-gray-6'],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M14.6,16.6L19.2,12L14.6,7.4L16,6L22,12L16,18L14.6,16.6M9.4,16.6L4.8,12L9.4,7.4L8,6L2,12L8,18L9.4,16.6Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click.prevent="
|
||||||
|
editor.chain().focus().toggleBlockquote().run()
|
||||||
|
"
|
||||||
|
title="blockquote"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
editor.isActive('blockquote')
|
||||||
|
? ['bg-sky-6', 'text-white']
|
||||||
|
: ['bg-white', 'text-gray-6'],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M10,7L8,11H11V17H5V11L7,7H10M18,7L16,11H19V17H13V11L15,7H18Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click.prevent="
|
||||||
|
editor.chain().focus().setHorizontalRule().run()
|
||||||
|
"
|
||||||
|
title="horizontal rule"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'bg-white',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path d="M19,13H5V11H19V13Z" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex p-1 border-r border-gray">
|
||||||
|
<button
|
||||||
|
@click.prevent="editor.chain().focus().setHardBreak().run()"
|
||||||
|
title="hard break"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
'bg-white',
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M10,11A4,4 0 0,1 6,7A4,4 0 0,1 10,3H18V5H16V21H14V5H12V21H10V11Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click.prevent="
|
||||||
|
editor.chain().focus().unsetAllMarks().run();
|
||||||
|
editor.chain().focus().clearNodes().run();
|
||||||
|
"
|
||||||
|
title="Clear formatting"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
'bg-white',
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M6,5V5.18L8.82,8H11.22L10.5,9.68L12.6,11.78L14.21,8H20V5H6M3.27,5L2,6.27L8.97,13.24L6.5,19H9.5L11.07,15.34L16.73,21L18,19.73L3.55,5.27L3.27,5Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex p-1">
|
||||||
|
<button
|
||||||
|
@click.prevent="editor.chain().focus().undo().run()"
|
||||||
|
title="Undo"
|
||||||
|
:disabled="!editor.can().chain().focus().undo().run()"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
'bg-white',
|
||||||
|
[
|
||||||
|
'disabled-text-gray',
|
||||||
|
'hover-disabled-bg-transparent',
|
||||||
|
'disabled-cursor-not-allowed',
|
||||||
|
],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>undo</title>
|
||||||
|
<path
|
||||||
|
d="M12.5,8C9.85,8 7.45,9 5.6,10.6L2,7V16H11L7.38,12.38C8.77,11.22 10.54,10.5 12.5,10.5C16.04,10.5 19.05,12.81 20.1,16L22.47,15.22C21.08,11.03 17.15,8 12.5,8Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click.prevent="editor.chain().focus().redo().run()"
|
||||||
|
title="Redo"
|
||||||
|
:disabled="!editor.can().chain().focus().redo().run()"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'flex-items-center',
|
||||||
|
'p-1',
|
||||||
|
'hover-bg-gray-3',
|
||||||
|
'bg-white',
|
||||||
|
[
|
||||||
|
'disabled-text-gray',
|
||||||
|
'hover-disabled-bg-transparent',
|
||||||
|
'disabled-cursor-not-allowed',
|
||||||
|
],
|
||||||
|
]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>redo</title>
|
||||||
|
<path
|
||||||
|
d="M18.4,10.6C16.55,9 14.15,8 11.5,8C6.85,8 2.92,11.03 1.54,15.22L3.9,16C4.95,12.81 7.95,10.5 11.5,10.5C13.45,10.5 15.23,11.22 16.62,12.38L13,16H22V7L18.4,10.6Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<EditorContent :editor="editor" />
|
<EditorContent
|
||||||
|
:editor="editor"
|
||||||
|
class="rounded-b-2 bg-white p-4 border-1 border-gray h-128 overflow-auto sm-editor" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -158,6 +389,9 @@
|
|||||||
import { onBeforeUnmount, watch } from "vue";
|
import { onBeforeUnmount, watch } from "vue";
|
||||||
import { useEditor, EditorContent } from "@tiptap/vue-3";
|
import { useEditor, EditorContent } from "@tiptap/vue-3";
|
||||||
import StarterKit from "@tiptap/starter-kit";
|
import StarterKit from "@tiptap/starter-kit";
|
||||||
|
import Underline from "@tiptap/extension-underline";
|
||||||
|
import TextAlign from "@tiptap/extension-text-align";
|
||||||
|
import { Info } from "../extensions/info";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@@ -170,12 +404,40 @@ const emits = defineEmits(["update:modelValue"]);
|
|||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
content: props.modelValue,
|
content: props.modelValue,
|
||||||
extensions: [StarterKit],
|
extensions: [StarterKit, Underline, TextAlign, Info],
|
||||||
onUpdate: () => {
|
onUpdate: () => {
|
||||||
emits("update:modelValue", editor.value.getHTML());
|
emits("update:modelValue", editor.value.getHTML());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updateNode = (event) => {
|
||||||
|
if (event.target.value) {
|
||||||
|
switch (event.target.value) {
|
||||||
|
case "paragraph":
|
||||||
|
editor.value.chain().focus().setParagraph().run();
|
||||||
|
break;
|
||||||
|
case "h1":
|
||||||
|
editor.value.chain().focus().setHeading({ level: 1 }).run();
|
||||||
|
break;
|
||||||
|
case "h2":
|
||||||
|
editor.value.chain().focus().setHeading({ level: 2 }).run();
|
||||||
|
break;
|
||||||
|
case "h3":
|
||||||
|
editor.value.chain().focus().setHeading({ level: 3 }).run();
|
||||||
|
break;
|
||||||
|
case "h4":
|
||||||
|
editor.value.chain().focus().setHeading({ level: 4 }).run();
|
||||||
|
break;
|
||||||
|
case "h5":
|
||||||
|
editor.value.chain().focus().setHeading({ level: 5 }).run();
|
||||||
|
break;
|
||||||
|
case "h6":
|
||||||
|
editor.value.chain().focus().setHeading({ level: 6 }).run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
editor.value.destroy();
|
editor.value.destroy();
|
||||||
});
|
});
|
||||||
@@ -193,46 +455,3 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.sm-editor {
|
|
||||||
.toolbar {
|
|
||||||
// display: flex;
|
|
||||||
// border-width: 1px 1px 0 1px;
|
|
||||||
// border-radius: 0.5rem 0.5rem 0 0;
|
|
||||||
// border-color: rgba(156, 163, 175, 1);
|
|
||||||
// background-color: rgba(255, 255, 255, 1);
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: flex;
|
|
||||||
background-color: rgba(255, 255, 255, 1);
|
|
||||||
color: rgba(75, 85, 99, 1);
|
|
||||||
padding: 0.25rem;
|
|
||||||
margin: 0.25rem;
|
|
||||||
background-color: rgba(255, 255, 255, 1);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
height: 1.2rem;
|
|
||||||
width: 1.2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button.is-active {
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ProseMirror {
|
|
||||||
border-width: 1px;
|
|
||||||
border-radius: 0 0 0.5rem 0.5rem;
|
|
||||||
border-color: rgba(156, 163, 175, 0.5) rgba(156, 163, 175, 1)
|
|
||||||
rgba(156, 163, 175, 1);
|
|
||||||
color: rgba(75, 85, 99, 1);
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgba(255, 255, 255, 1);
|
|
||||||
height: 24rem;
|
|
||||||
overflow: scroll;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
60
resources/js/extensions/info.ts
Normal file
60
resources/js/extensions/info.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { mergeAttributes, Node } from "@tiptap/core";
|
||||||
|
|
||||||
|
export interface InfoOptions {
|
||||||
|
HTMLAttributes: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "@tiptap/core" {
|
||||||
|
interface Commands<ReturnType> {
|
||||||
|
info: {
|
||||||
|
/**
|
||||||
|
* Toggle a paragraph
|
||||||
|
*/
|
||||||
|
setInfo: () => ReturnType;
|
||||||
|
toggleInfo: () => ReturnType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Info = Node.create<InfoOptions>({
|
||||||
|
name: "info",
|
||||||
|
|
||||||
|
priority: 1000,
|
||||||
|
|
||||||
|
addOptions() {
|
||||||
|
return {
|
||||||
|
HTMLAttributes: { class: "info" },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
group: "block",
|
||||||
|
|
||||||
|
content: "inline*",
|
||||||
|
|
||||||
|
parseHTML() {
|
||||||
|
return [{ tag: "p", class: "info" }];
|
||||||
|
},
|
||||||
|
|
||||||
|
renderHTML({ HTMLAttributes }) {
|
||||||
|
return [
|
||||||
|
"p",
|
||||||
|
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
||||||
|
0,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
addCommands() {
|
||||||
|
return {
|
||||||
|
setInfo:
|
||||||
|
() =>
|
||||||
|
({ commands }) => {
|
||||||
|
return commands.setNode(this.name);
|
||||||
|
},
|
||||||
|
toggleInfo:
|
||||||
|
() =>
|
||||||
|
({ commands }) => {
|
||||||
|
return commands.toggleNode(this.name, "paragraph");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -38,6 +38,58 @@
|
|||||||
.scrollbar-width-none { scrollbar-width: none; }
|
.scrollbar-width-none { scrollbar-width: none; }
|
||||||
.scrollbar-width-none::-webkit-scrollbar { display: none; }
|
.scrollbar-width-none::-webkit-scrollbar { display: none; }
|
||||||
.spin{animation:rotate 1s infinite linear}
|
.spin{animation:rotate 1s infinite linear}
|
||||||
|
.sm-html .ProseMirror {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-html hr {
|
||||||
|
border-top: 1px solid #aaa;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-html pre {
|
||||||
|
padding: 0 1rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-html blockquote {
|
||||||
|
border-left: 4px solid #ddd;
|
||||||
|
margin-left: 1rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-html p.info {
|
||||||
|
display: flex;
|
||||||
|
border: 1px solid rgba(14,165,233,1);
|
||||||
|
background-color: rgba(14,165,233,0.25);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem 0.5rem 0.75rem;
|
||||||
|
margin: 0.5rem;
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-html p.info::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z' fill='currentColor' /%3E%3C/svg%3E");
|
||||||
|
display: inline-block;
|
||||||
|
color: rgba(14,165,233,1);
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
margin-top: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-editor::-webkit-scrollbar {
|
||||||
|
background-color: transparent;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-editor::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #aaa;
|
||||||
|
border: 4px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes rotate{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
|
@keyframes rotate{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Reference in New Issue
Block a user