tiptapify 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -3
- package/dist/tiptapify.css +1 -1
- package/dist/tiptapify.mjs +27785 -23593
- package/dist/tiptapify.umd.js +35 -35
- package/index.d.ts +49 -0
- package/package.json +8 -6
- package/src/components/Footer.vue +48 -6
- package/src/components/MenuBubble.vue +5 -12
- package/src/components/Tiptapify.vue +41 -31
- package/src/components/Toolbar/Group.vue +13 -14
- package/src/components/Toolbar/GroupBtn.vue +34 -0
- package/src/components/Toolbar/Index.vue +28 -79
- package/src/components/Toolbar/Items.vue +77 -0
- package/src/components/Toolbar/defaultExtensionComponents.ts +32 -0
- package/src/components/Toolbar/items/format.ts +0 -13
- package/src/components/Toolbar/items/media.ts +32 -23
- package/src/components/Toolbar/items/misc.ts +1 -1
- package/src/components/Toolbar/items/style.ts +43 -2
- package/src/components/Toolbar/items.ts +8 -7
- package/src/components/editorExtensions.ts +8 -10
- package/src/extensions/components/ImageDialog.vue +142 -0
- package/src/extensions/components/LinkDialog.vue +77 -36
- package/src/extensions/components/PreviewDialog.vue +0 -1
- package/src/extensions/components/ShowSourceDialog.vue +5 -3
- package/src/extensions/components/StyleColor.vue +177 -0
- package/src/extensions/components/TableBuilder.vue +4 -5
- package/src/extensions/image.ts +31 -0
- package/src/extensions/link.ts +24 -0
- package/src/extensions/preview.ts +2 -15
- package/src/extensions/view-source.ts +0 -3
- package/src/i18n/locales/de.json +46 -20
- package/src/i18n/locales/en.json +31 -5
- package/src/i18n/locales/es.json +38 -12
- package/src/i18n/locales/fr.json +49 -23
- package/src/i18n/locales/it.json +42 -16
- package/src/i18n/locales/pl.json +46 -19
- package/src/i18n/locales/ru.json +30 -4
- package/src/i18n/locales/ua.json +30 -4
- package/src/index.ts +0 -1
- package/src/types/overridable-extensions.ts +6 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Editor } from "@tiptap/vue-3";
|
|
2
|
+
import type { DefineComponent } from 'vue'
|
|
3
|
+
import { extensionsComponents } from "./src/types/overridable-extensions";
|
|
4
|
+
|
|
5
|
+
export interface TiptapifyProps {
|
|
6
|
+
content: string|object
|
|
7
|
+
variantBtn: string
|
|
8
|
+
variantField: string
|
|
9
|
+
toolbar: boolean
|
|
10
|
+
items: [string]
|
|
11
|
+
itemsExclude: boolean
|
|
12
|
+
bubbleMenu: boolean
|
|
13
|
+
floatingMenu: boolean
|
|
14
|
+
slashCommands: boolean
|
|
15
|
+
placeholder: string
|
|
16
|
+
showWordsCount: boolean
|
|
17
|
+
showCharactersCount: boolean
|
|
18
|
+
defaultFontFamily: string
|
|
19
|
+
fontMeasure: string
|
|
20
|
+
rounded: string
|
|
21
|
+
overrideExtensionsComponents: extensionsComponents
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TiptapifyEmits {
|
|
25
|
+
'editor-ready': (options: {
|
|
26
|
+
getHTML: () => string
|
|
27
|
+
getJSON: () => any
|
|
28
|
+
editor: Editor
|
|
29
|
+
}) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export declare const Tiptapify: DefineComponent<TiptapifyProps, {}, {}, {}, {}, {}, {}, TiptapifyEmits>
|
|
33
|
+
|
|
34
|
+
// Плагин
|
|
35
|
+
export interface TiptapifyOptions {
|
|
36
|
+
locale?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare const TiptapifyPlugin: {
|
|
40
|
+
install: (app: any, options?: TiptapifyOptions) => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default TiptapifyPlugin
|
|
44
|
+
|
|
45
|
+
declare module '@vue/runtime-core' {
|
|
46
|
+
interface GlobalComponents {
|
|
47
|
+
Tiptapify: typeof Tiptapify
|
|
48
|
+
}
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiptapify",
|
|
3
|
-
"
|
|
3
|
+
"types": "./index.d.ts",
|
|
4
|
+
"version": "0.0.9",
|
|
4
5
|
"description": "Tiptap3 editor with Vuetify3 menu implementation",
|
|
5
6
|
"exports": {
|
|
6
7
|
".": {
|
|
@@ -22,7 +23,8 @@
|
|
|
22
23
|
"source": "./src/index.ts",
|
|
23
24
|
"files": [
|
|
24
25
|
"dist",
|
|
25
|
-
"src"
|
|
26
|
+
"src",
|
|
27
|
+
"index.d.ts"
|
|
26
28
|
],
|
|
27
29
|
"scripts": {
|
|
28
30
|
"dev": "vite",
|
|
@@ -49,7 +51,7 @@
|
|
|
49
51
|
"author": "Igor Voytovich",
|
|
50
52
|
"license": "MIT",
|
|
51
53
|
"repository": "https://github.com/IVoyt/tiptapify",
|
|
52
|
-
"packageManager": "pnpm@10.
|
|
54
|
+
"packageManager": "pnpm@10.12.1",
|
|
53
55
|
"dependencies": {
|
|
54
56
|
"@tiptap/core": "next",
|
|
55
57
|
"@tiptap/extension-blockquote": "next",
|
|
@@ -92,7 +94,7 @@
|
|
|
92
94
|
"highlight.js": "^11.11.1",
|
|
93
95
|
"linkifyjs": "^4.3.1",
|
|
94
96
|
"lowlight": "^3.3.0",
|
|
95
|
-
"vue-i18n": "^11.1.
|
|
97
|
+
"vue-i18n": "^11.1.6"
|
|
96
98
|
},
|
|
97
99
|
"peerDependencies": {
|
|
98
100
|
"@mdi/js": "^7.4.47",
|
|
@@ -102,11 +104,11 @@
|
|
|
102
104
|
"devDependencies": {
|
|
103
105
|
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
|
104
106
|
"@rollup/plugin-alias": "^5.1.1",
|
|
105
|
-
"@types/node": "^22.15.
|
|
107
|
+
"@types/node": "^22.15.32",
|
|
106
108
|
"@vitejs/plugin-vue": "^5.2.4",
|
|
107
109
|
"@vitejs/plugin-vue-jsx": "^4.2.0",
|
|
108
110
|
"rollup-plugin-tsconfig-paths": "^1.5.2",
|
|
109
|
-
"sass-embedded": "^1.89.
|
|
111
|
+
"sass-embedded": "^1.89.2",
|
|
110
112
|
"typescript": "^5.8.3",
|
|
111
113
|
"unplugin-vue-components": "^28.7.0",
|
|
112
114
|
"vite": "^6.3.5",
|
|
@@ -1,17 +1,51 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Editor } from "@tiptap/vue-3";
|
|
3
|
-
import { inject, Ref } from "vue";
|
|
3
|
+
import { computed, inject, ref, Ref } from "vue";
|
|
4
|
+
|
|
5
|
+
import { useI18n } from "vue-i18n";
|
|
6
|
+
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
showWordsCount: { type: Boolean, default: true },
|
|
9
|
+
showCharactersCount: { type: Boolean, default: true },
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const { t } = useI18n()
|
|
4
13
|
|
|
5
14
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
15
|
+
|
|
16
|
+
const statusItems = ref([
|
|
17
|
+
{
|
|
18
|
+
enabled: computed(() => props.showWordsCount),
|
|
19
|
+
text: 'footer.words',
|
|
20
|
+
value: computed(() => editor.value.storage.characterCount.words())
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
enabled: computed(() => props.showCharactersCount),
|
|
24
|
+
text: 'footer.chars',
|
|
25
|
+
value: computed(() => editor.value.storage.characterCount.characters())
|
|
26
|
+
}
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
function printStatusItemText(text: string): string {
|
|
30
|
+
return t(text)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function showFooter() {
|
|
34
|
+
return editor.value && (props.showWordsCount || props.showCharactersCount)
|
|
35
|
+
}
|
|
6
36
|
</script>
|
|
7
37
|
|
|
8
38
|
<template>
|
|
9
|
-
<div v-if="
|
|
39
|
+
<div v-if="showFooter()" class="tiptapify-footer">
|
|
10
40
|
<VRow>
|
|
11
41
|
<VCol class="d-flex justify-end">
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
42
|
+
<template v-for="statusItem in statusItems" :key="statusItem.text">
|
|
43
|
+
<span v-if="statusItem.enabled" class="tiptapify-footer--status-item">
|
|
44
|
+
{{ printStatusItemText(statusItem.text) }}: {{ statusItem.value }}
|
|
45
|
+
</span>
|
|
46
|
+
|
|
47
|
+
<VDivider class="tiptapify-footer--divider" vertical />
|
|
48
|
+
</template>
|
|
15
49
|
</VCol>
|
|
16
50
|
</VRow>
|
|
17
51
|
</div>
|
|
@@ -23,7 +57,15 @@ const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
|
23
57
|
border-top: var(--border);
|
|
24
58
|
}
|
|
25
59
|
|
|
26
|
-
.
|
|
60
|
+
.tiptapify-footer--status-item {
|
|
27
61
|
color: #999;
|
|
28
62
|
}
|
|
63
|
+
|
|
64
|
+
.tiptapify-footer--divider:not(:last-child) {
|
|
65
|
+
margin: 0 8px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.tiptapify-footer--divider:last-child {
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
29
71
|
</style>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Editor } from "@tiptap/vue-3";
|
|
3
|
-
import LinkDialog from "@tiptapify/extensions/components/LinkDialog.vue";
|
|
4
3
|
import { computed, defineProps, inject, Ref, ref } from "vue";
|
|
5
4
|
import { BubbleMenu } from '@tiptap/vue-3/menus'
|
|
6
5
|
import * as mdi from '@mdi/js'
|
|
@@ -12,8 +11,6 @@ defineProps({
|
|
|
12
11
|
|
|
13
12
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
14
13
|
|
|
15
|
-
const bubbleMenuLinkButton = ref(null)
|
|
16
|
-
|
|
17
14
|
const items = ref([
|
|
18
15
|
{
|
|
19
16
|
name: 'bold',
|
|
@@ -76,7 +73,7 @@ const items = ref([
|
|
|
76
73
|
color: computed(() => editor.value.isActive('link') ? 'primary' : ''),
|
|
77
74
|
disabled: computed(() => editor.value.isActive('code') || editor.value.isActive('codeBlock')),
|
|
78
75
|
},
|
|
79
|
-
click: () =>
|
|
76
|
+
click: () => editor.value.commands.showLink()
|
|
80
77
|
},
|
|
81
78
|
{
|
|
82
79
|
name: 'format clear',
|
|
@@ -84,12 +81,6 @@ const items = ref([
|
|
|
84
81
|
click: () => editor.value.chain().focus().unsetAllMarks().clearNodes().run(),
|
|
85
82
|
}
|
|
86
83
|
])
|
|
87
|
-
|
|
88
|
-
function linkAction() {
|
|
89
|
-
return editor.value.isActive('link')
|
|
90
|
-
? editor.value.chain().focus().unsetLink().run()
|
|
91
|
-
: bubbleMenuLinkButton.value?.open()
|
|
92
|
-
}
|
|
93
84
|
</script>
|
|
94
85
|
|
|
95
86
|
<template>
|
|
@@ -98,6 +89,10 @@ function linkAction() {
|
|
|
98
89
|
:editor="editor"
|
|
99
90
|
:options="{ placement: 'bottom' }"
|
|
100
91
|
:shouldShow="({ editor, view, state, from, to }) => {
|
|
92
|
+
if (editor.isActive('image') || editor.isActive('code') || editor.isActive('codeBlock')) {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
101
96
|
const docSize = editor.state.doc.content.size
|
|
102
97
|
const isAllSelected = from === 0 && to === docSize
|
|
103
98
|
|
|
@@ -120,8 +115,6 @@ function linkAction() {
|
|
|
120
115
|
</VCard>
|
|
121
116
|
</div>
|
|
122
117
|
</BubbleMenu>
|
|
123
|
-
|
|
124
|
-
<LinkDialog ref="bubbleMenuLinkButton" />
|
|
125
118
|
</template>
|
|
126
119
|
|
|
127
120
|
<style scoped lang="scss">
|
|
@@ -1,36 +1,49 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { extensionsComponents } from "@tiptapify/types/overridable-extensions";
|
|
4
|
+
import { computed, onBeforeUnmount, PropType, provide, ref, ShallowRef, watch } from "vue";
|
|
4
5
|
import { default as Toolbar } from "@tiptapify/components/Toolbar/Index.vue";
|
|
5
6
|
import { Editor, EditorContent } from '@tiptap/vue-3'
|
|
6
7
|
import MenuBubble from '@tiptapify/components/MenuBubble.vue'
|
|
7
8
|
import MenuFloating from '@tiptapify/components/MenuFloating.vue'
|
|
8
9
|
|
|
10
|
+
import { useI18n } from "vue-i18n";
|
|
11
|
+
|
|
9
12
|
import { getTiptapEditor } from "@tiptapify/components/index";
|
|
10
13
|
|
|
11
14
|
import Footer from '@tiptapify/components/Footer.vue'
|
|
12
15
|
import { useTheme } from "vuetify/framework";
|
|
13
16
|
|
|
17
|
+
const { t } = useI18n();
|
|
18
|
+
|
|
14
19
|
const props = defineProps({
|
|
15
20
|
content: String|Object,
|
|
16
|
-
|
|
21
|
+
variantBtn: { type: String, default () { return 'elevated' } },
|
|
22
|
+
variantField: { type: String, default () { return 'solo' } },
|
|
17
23
|
toolbar: { type: Boolean, default () { return true } },
|
|
18
24
|
items: { type: Array<string>, default() { return [] }},
|
|
19
25
|
itemsExclude: { type: Boolean, default() { return false } },
|
|
20
26
|
bubbleMenu: { type: Boolean, default () { return true } },
|
|
21
27
|
floatingMenu: { type: Boolean, default () { return true } },
|
|
22
28
|
slashCommands: { type: Boolean, default () { return true } },
|
|
23
|
-
placeholder: { type: String, default () { return '
|
|
24
|
-
|
|
29
|
+
placeholder: { type: String, default () { return '' } },
|
|
30
|
+
showWordsCount: { type: Boolean, default () { return true } },
|
|
31
|
+
showCharactersCount: { type: Boolean, default () { return true } },
|
|
25
32
|
defaultFontFamily: { type: String, default () { return 'Inter' } },
|
|
26
33
|
fontMeasure: { type: String, default () { return 'px' } },
|
|
27
34
|
rounded: { type: String, default () { return '0' } },
|
|
35
|
+
overrideExtensionsComponents: { type: Object as PropType<extensionsComponents>, default() { return {} } },
|
|
28
36
|
})
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
// console.log('override extension components', computed(() => props.overrideExtensionsComponents).value)
|
|
31
39
|
|
|
32
|
-
const
|
|
33
|
-
|
|
40
|
+
const appTheme = useTheme()
|
|
41
|
+
const currentTheme = ref(appTheme.global.name)
|
|
42
|
+
|
|
43
|
+
const editor: ShallowRef<Editor | undefined> = getTiptapEditor(
|
|
44
|
+
props.content,
|
|
45
|
+
computed(() => props.placeholder || t('content.placeholder')).value,
|
|
46
|
+
props.slashCommands
|
|
34
47
|
)
|
|
35
48
|
|
|
36
49
|
const emit = defineEmits(['update:modelValue', 'editor-ready']);
|
|
@@ -39,11 +52,10 @@ provide('tiptapifyEditor', editor)
|
|
|
39
52
|
|
|
40
53
|
editor.value?.chain().setFontFamily(props.defaultFontFamily).run()
|
|
41
54
|
|
|
42
|
-
defineExpose({ editor: editor });
|
|
43
|
-
|
|
44
55
|
watch(() => editor.value, (editorInstance) => {
|
|
45
56
|
if (editorInstance instanceof Editor) {
|
|
46
57
|
emit('editor-ready', {
|
|
58
|
+
editor: editorInstance,
|
|
47
59
|
getHTML: () => editorInstance.getHTML(),
|
|
48
60
|
getJSON: () => editorInstance.getJSON(),
|
|
49
61
|
});
|
|
@@ -62,26 +74,27 @@ onBeforeUnmount(() => {
|
|
|
62
74
|
<template v-if="toolbar">
|
|
63
75
|
<Toolbar
|
|
64
76
|
v-if="editor"
|
|
65
|
-
:variant="
|
|
77
|
+
:variant-btn="variantBtn"
|
|
78
|
+
:variant-field="variantField"
|
|
66
79
|
:font-measure="fontMeasure"
|
|
67
80
|
:items="items"
|
|
68
81
|
:items-exclude="itemsExclude"
|
|
69
82
|
:rounded="rounded"
|
|
83
|
+
:override-extensions-components="overrideExtensionsComponents"
|
|
84
|
+
:theme="currentTheme"
|
|
70
85
|
/>
|
|
71
86
|
</template>
|
|
72
87
|
|
|
73
88
|
<div :class="`border border-t-0 rounded-b-${rounded}`">
|
|
74
89
|
<div class="pa-2 tiptapify-container">
|
|
75
|
-
<MenuFloating v-if="floatingMenu" :variant="
|
|
90
|
+
<MenuFloating v-if="floatingMenu" :variant="variantBtn" :theme="currentTheme" />
|
|
76
91
|
|
|
77
|
-
<MenuBubble v-if="bubbleMenu" :variant="
|
|
92
|
+
<MenuBubble v-if="bubbleMenu" :variant="variantBtn" :theme="currentTheme" />
|
|
78
93
|
|
|
79
94
|
<EditorContent :editor="editor" class="tiptapify-editor" />
|
|
80
95
|
</div>
|
|
81
96
|
|
|
82
|
-
<
|
|
83
|
-
<Footer />
|
|
84
|
-
</template>
|
|
97
|
+
<Footer :show-words-count="showWordsCount" :show-characters-count="showCharactersCount" />
|
|
85
98
|
</div>
|
|
86
99
|
</VCol>
|
|
87
100
|
</VRow>
|
|
@@ -133,7 +146,6 @@ onBeforeUnmount(() => {
|
|
|
133
146
|
background-repeat: no-repeat;
|
|
134
147
|
background-position: right .1rem center;
|
|
135
148
|
background-size: 1.25rem 1.25rem;
|
|
136
|
-
/* padding-right: 1.25rem; */
|
|
137
149
|
|
|
138
150
|
border-radius: .5rem;
|
|
139
151
|
border: none;
|
|
@@ -148,8 +160,7 @@ onBeforeUnmount(() => {
|
|
|
148
160
|
}
|
|
149
161
|
|
|
150
162
|
/* List styles */
|
|
151
|
-
ul,
|
|
152
|
-
ol {
|
|
163
|
+
ul, ol {
|
|
153
164
|
padding: 0 1rem;
|
|
154
165
|
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
|
155
166
|
|
|
@@ -190,19 +201,13 @@ onBeforeUnmount(() => {
|
|
|
190
201
|
}
|
|
191
202
|
|
|
192
203
|
/* Heading styles */
|
|
193
|
-
h1,
|
|
194
|
-
h2,
|
|
195
|
-
h3,
|
|
196
|
-
h4,
|
|
197
|
-
h5,
|
|
198
|
-
h6 {
|
|
204
|
+
h1, h2, h3, h4, h5, h6 {
|
|
199
205
|
line-height: 1.1;
|
|
200
206
|
margin-top: 2.5rem;
|
|
201
207
|
text-wrap: pretty;
|
|
202
208
|
}
|
|
203
209
|
|
|
204
|
-
h1,
|
|
205
|
-
h2 {
|
|
210
|
+
h1, h2 {
|
|
206
211
|
margin-top: 3.5rem;
|
|
207
212
|
margin-bottom: 1.5rem;
|
|
208
213
|
}
|
|
@@ -219,9 +224,7 @@ onBeforeUnmount(() => {
|
|
|
219
224
|
font-size: 1.1rem;
|
|
220
225
|
}
|
|
221
226
|
|
|
222
|
-
h4,
|
|
223
|
-
h5,
|
|
224
|
-
h6 {
|
|
227
|
+
h4, h5, h6 {
|
|
225
228
|
font-size: 1rem;
|
|
226
229
|
}
|
|
227
230
|
|
|
@@ -323,8 +326,7 @@ onBeforeUnmount(() => {
|
|
|
323
326
|
table-layout: fixed;
|
|
324
327
|
width: 100%;
|
|
325
328
|
|
|
326
|
-
td,
|
|
327
|
-
th {
|
|
329
|
+
td, th {
|
|
328
330
|
border: 1px solid var(--gray-3);
|
|
329
331
|
box-sizing: border-box;
|
|
330
332
|
min-width: 1em;
|
|
@@ -366,6 +368,14 @@ onBeforeUnmount(() => {
|
|
|
366
368
|
}
|
|
367
369
|
}
|
|
368
370
|
|
|
371
|
+
p.is-editor-empty:first-child::before {
|
|
372
|
+
color: var(--gray-4);
|
|
373
|
+
content: attr(data-placeholder);
|
|
374
|
+
float: left;
|
|
375
|
+
height: 0;
|
|
376
|
+
pointer-events: none;
|
|
377
|
+
}
|
|
378
|
+
|
|
369
379
|
.tableWrapper {
|
|
370
380
|
margin: 1.5rem 0;
|
|
371
381
|
overflow-x: auto;
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import GroupBtn from "@tiptapify/components/Toolbar/GroupBtn.vue";
|
|
2
3
|
import GroupDropdown from "@tiptapify/components/Toolbar/GroupDropdown.vue";
|
|
3
4
|
import { defineProps, PropType } from 'vue'
|
|
4
|
-
import { useI18n } from "vue-i18n";
|
|
5
5
|
|
|
6
6
|
import { ToolbarItemSection } from "@tiptapify/components/Toolbar/items";
|
|
7
7
|
|
|
8
|
-
import helpers from "@tiptapify/utils/helpers";
|
|
9
|
-
|
|
10
8
|
defineProps({
|
|
11
9
|
variant: { type: String, default () { return 'flat' }},
|
|
12
10
|
section: { type: String, default() { return '' }},
|
|
13
11
|
toolbarSection: { type: Object as PropType<ToolbarItemSection>, default() { return {} }}
|
|
14
12
|
})
|
|
15
13
|
|
|
16
|
-
const { t } = useI18n();
|
|
17
|
-
|
|
18
|
-
const { ucFirst } = helpers;
|
|
19
|
-
|
|
20
14
|
</script>
|
|
21
15
|
|
|
22
16
|
<template>
|
|
@@ -26,14 +20,19 @@ const { ucFirst } = helpers;
|
|
|
26
20
|
<GroupDropdown :toolbar-item="toolbarItem" :variant="variant" />
|
|
27
21
|
</template>
|
|
28
22
|
|
|
29
|
-
<
|
|
30
|
-
<
|
|
23
|
+
<VMenu v-else-if="toolbarItem?.component" v-model="toolbarItem.modelValue" :close-on-content-click="false">
|
|
24
|
+
<template #activator="{ props: menuProps }">
|
|
25
|
+
<GroupBtn v-bind="menuProps" :toolbar-item="toolbarItem" :variant="variant" />
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<component
|
|
29
|
+
:is="toolbarItem.component"
|
|
30
|
+
v-bind="toolbarItem?.componentProps ?? {}"
|
|
31
|
+
@close="toolbarItem.modelValue = false"
|
|
32
|
+
/>
|
|
33
|
+
</VMenu>
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
<span v-else class="menu-item-title">
|
|
34
|
-
{{ ucFirst(t(toolbarItem.name)) }}
|
|
35
|
-
</span>
|
|
36
|
-
</VBtn>
|
|
35
|
+
<GroupBtn v-else :toolbar-item="toolbarItem" :variant="variant" />
|
|
37
36
|
</template>
|
|
38
37
|
</VBtnGroup>
|
|
39
38
|
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { defineProps, PropType } from 'vue'
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
|
|
5
|
+
import { ToolbarItem } from "@tiptapify/components/Toolbar/items";
|
|
6
|
+
|
|
7
|
+
import helpers from "@tiptapify/utils/helpers";
|
|
8
|
+
|
|
9
|
+
defineProps({
|
|
10
|
+
variant: { type: String, default () { return 'flat' }},
|
|
11
|
+
toolbarItem: { type: Object as PropType<ToolbarItem>, default() { return {} }}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const { t } = useI18n();
|
|
15
|
+
|
|
16
|
+
const { ucFirst } = helpers;
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<VBtn v-bind="toolbarItem?.props ?? {}" v-on="toolbarItem?.attrs ?? {}" size="32">
|
|
21
|
+
<VTooltip :text="ucFirst(t(toolbarItem.tooltip))" location="top" activator="parent" />
|
|
22
|
+
|
|
23
|
+
<VIcon v-if="toolbarItem.icon" :icon="toolbarItem.icon" size="small" />
|
|
24
|
+
<span v-else class="menu-item-title">
|
|
25
|
+
{{ ucFirst(t(toolbarItem.name)) }}
|
|
26
|
+
</span>
|
|
27
|
+
|
|
28
|
+
<VIcon v-if="toolbarItem.icon2" v-bind="toolbarItem?.icon2Props ?? {}" :icon="toolbarItem.icon2" size="small" style="position: absolute;" />
|
|
29
|
+
</VBtn>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<style lang="scss" scoped>
|
|
33
|
+
|
|
34
|
+
</style>
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Editor } from "@tiptap/vue-3";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import Toggle from "@tiptapify/components/Toolbar/Toggle.vue";
|
|
8
|
-
import { computed, defineProps, inject, Ref, ref } from 'vue'
|
|
9
|
-
import { useI18n } from "vue-i18n";
|
|
3
|
+
import { getDefaultComponents } from "@tiptapify/components/Toolbar/defaultExtensionComponents";
|
|
4
|
+
import Items from "@tiptapify/components/Toolbar/Items.vue";
|
|
5
|
+
import { computed, defineProps, inject, PropType, ref, Ref, ShallowRef, shallowRef, triggerRef } from 'vue'
|
|
6
|
+
import { extensionsComponents } from '@tiptapify/types/overridable-extensions'
|
|
10
7
|
|
|
11
|
-
import { toolbarItems
|
|
8
|
+
import { toolbarItems } from "@tiptapify/components/Toolbar/items";
|
|
9
|
+
import { useTheme } from "vuetify/framework";
|
|
12
10
|
|
|
13
11
|
const props = defineProps({
|
|
14
|
-
|
|
12
|
+
variantBtn: { type: String, default () { return 'elevated' }},
|
|
13
|
+
variantField: { type: String, default () { return 'solo' }},
|
|
15
14
|
items: { type: Array<string>, default() { return [] }},
|
|
16
15
|
itemsExclude: { type: Boolean, default() { return false } },
|
|
17
16
|
headingLevels: { type: Array<number>, default() { return [] }},
|
|
@@ -20,97 +19,47 @@ const props = defineProps({
|
|
|
20
19
|
customFontsOverride: { type: Boolean, default() { return false } },
|
|
21
20
|
theme: { type: String, default() { return 'light' } },
|
|
22
21
|
rounded: { type: String, default() { return '0' } },
|
|
22
|
+
toolbarScrollable: { type: Boolean, default() { return false } },
|
|
23
|
+
overrideExtensionsComponents: { type: Object as PropType<extensionsComponents>, default() { return {} } },
|
|
23
24
|
})
|
|
24
25
|
|
|
25
|
-
const { t } = useI18n();
|
|
26
|
-
|
|
27
26
|
const editor = inject('tiptapifyEditor') as Ref<Editor>
|
|
28
27
|
|
|
29
|
-
const
|
|
28
|
+
const theme = useTheme()
|
|
30
29
|
|
|
31
30
|
const items = toolbarItems(
|
|
32
31
|
editor,
|
|
32
|
+
theme,
|
|
33
33
|
computed(() => props.fontMeasure).value,
|
|
34
34
|
{ list: computed(() => props.items).value, exclude: computed(() => props.itemsExclude).value },
|
|
35
|
-
computed(() => props.headingLevels).value
|
|
36
|
-
toolbarLinkButton
|
|
35
|
+
computed(() => props.headingLevels).value
|
|
37
36
|
)
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
const defaultComponents: extensionsComponents = getDefaultComponents(props.variantField)
|
|
39
|
+
|
|
40
|
+
const extensions: ShallowRef<extensionsComponents> = shallowRef({})
|
|
41
|
+
Object.keys(defaultComponents).forEach(extension => {
|
|
42
|
+
extensions.value[extension] = props.overrideExtensionsComponents[extension] ?? defaultComponents[extension]
|
|
43
|
+
})
|
|
39
44
|
|
|
40
45
|
</script>
|
|
41
46
|
|
|
42
47
|
<template>
|
|
43
48
|
<div v-if="editor">
|
|
44
|
-
<VToolbar elevation="1" :theme="theme" height="auto" :class="`ps-1 rounded-t-${rounded}`">
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<Toggle v-else-if="toolbarSection.toggle" :variant="variant" :toolbar-section="toolbarSection" />
|
|
50
|
-
|
|
51
|
-
<VBtn
|
|
52
|
-
v-else
|
|
53
|
-
v-for="(toolbarItem, itemKey) in toolbarSection.items"
|
|
54
|
-
:key="itemKey"
|
|
55
|
-
:variant="variant"
|
|
56
|
-
v-bind="toolbarItem.props"
|
|
57
|
-
v-on="toolbarItem.attrs"
|
|
58
|
-
class="menu-button"
|
|
59
|
-
size="32"
|
|
60
|
-
elevation="4"
|
|
61
|
-
rounded="sm"
|
|
62
|
-
>
|
|
63
|
-
<VTooltip :text="t(toolbarItem.tooltip)" location="top" activator="parent" />
|
|
64
|
-
|
|
65
|
-
<VIcon v-if="toolbarItem.icon" :icon="toolbarItem.icon" size="16" />
|
|
66
|
-
<span v-else class="menu-item-title">
|
|
67
|
-
{{ t(toolbarItem.name) }}
|
|
68
|
-
</span>
|
|
69
|
-
</VBtn>
|
|
49
|
+
<VToolbar elevation="1" :theme="theme" height="auto" :class="`ps-1 pr-1 rounded-t-${rounded}`">
|
|
50
|
+
<VSlideGroup v-if="toolbarScrollable">
|
|
51
|
+
<Items :items="items" />
|
|
52
|
+
</VSlideGroup>
|
|
70
53
|
|
|
71
|
-
|
|
72
|
-
</template>
|
|
73
|
-
</VToolbarItems>
|
|
54
|
+
<Items v-else :items="items" />
|
|
74
55
|
</VToolbar>
|
|
75
56
|
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
57
|
+
<template v-for="extension in extensions">
|
|
58
|
+
<component :is="extension.component" v-bind="extension?.props ?? {}" />
|
|
59
|
+
</template>
|
|
79
60
|
</div>
|
|
80
61
|
</template>
|
|
81
62
|
|
|
82
63
|
<style lang="scss" scoped>
|
|
83
|
-
.tiptapify-menu {
|
|
84
|
-
padding: 8px;
|
|
85
|
-
border-bottom: var(--border);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
:deep(.toolbar__items) {
|
|
89
|
-
flex-wrap: wrap;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
:deep(.v-btn-group) {
|
|
93
|
-
height: 32px !important;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.menu-item-title {
|
|
97
|
-
font-size: 14px;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.menu-button {
|
|
101
|
-
margin: 0 1px;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.menu-divider {
|
|
105
|
-
margin: 0 4px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.menu-divider:nth-last-child(1) {
|
|
109
|
-
display: none;
|
|
110
|
-
}
|
|
111
64
|
|
|
112
|
-
.v-toolbar-items {
|
|
113
|
-
flex-wrap: wrap;
|
|
114
|
-
row-gap: 5px;
|
|
115
|
-
}
|
|
116
65
|
</style>
|