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.
Files changed (40) hide show
  1. package/README.md +27 -3
  2. package/dist/tiptapify.css +1 -1
  3. package/dist/tiptapify.mjs +27785 -23593
  4. package/dist/tiptapify.umd.js +35 -35
  5. package/index.d.ts +49 -0
  6. package/package.json +8 -6
  7. package/src/components/Footer.vue +48 -6
  8. package/src/components/MenuBubble.vue +5 -12
  9. package/src/components/Tiptapify.vue +41 -31
  10. package/src/components/Toolbar/Group.vue +13 -14
  11. package/src/components/Toolbar/GroupBtn.vue +34 -0
  12. package/src/components/Toolbar/Index.vue +28 -79
  13. package/src/components/Toolbar/Items.vue +77 -0
  14. package/src/components/Toolbar/defaultExtensionComponents.ts +32 -0
  15. package/src/components/Toolbar/items/format.ts +0 -13
  16. package/src/components/Toolbar/items/media.ts +32 -23
  17. package/src/components/Toolbar/items/misc.ts +1 -1
  18. package/src/components/Toolbar/items/style.ts +43 -2
  19. package/src/components/Toolbar/items.ts +8 -7
  20. package/src/components/editorExtensions.ts +8 -10
  21. package/src/extensions/components/ImageDialog.vue +142 -0
  22. package/src/extensions/components/LinkDialog.vue +77 -36
  23. package/src/extensions/components/PreviewDialog.vue +0 -1
  24. package/src/extensions/components/ShowSourceDialog.vue +5 -3
  25. package/src/extensions/components/StyleColor.vue +177 -0
  26. package/src/extensions/components/TableBuilder.vue +4 -5
  27. package/src/extensions/image.ts +31 -0
  28. package/src/extensions/link.ts +24 -0
  29. package/src/extensions/preview.ts +2 -15
  30. package/src/extensions/view-source.ts +0 -3
  31. package/src/i18n/locales/de.json +46 -20
  32. package/src/i18n/locales/en.json +31 -5
  33. package/src/i18n/locales/es.json +38 -12
  34. package/src/i18n/locales/fr.json +49 -23
  35. package/src/i18n/locales/it.json +42 -16
  36. package/src/i18n/locales/pl.json +46 -19
  37. package/src/i18n/locales/ru.json +30 -4
  38. package/src/i18n/locales/ua.json +30 -4
  39. package/src/index.ts +0 -1
  40. 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
- "version": "0.0.7",
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.11.1",
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.5"
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.30",
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.1",
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="editor" class="tiptapify-footer">
39
+ <div v-if="showFooter()" class="tiptapify-footer">
10
40
  <VRow>
11
41
  <VCol class="d-flex justify-end">
12
- <span class="words-count">
13
- {{ editor.storage.characterCount.words() }} words
14
- </span>
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
- .words-count {
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: () => linkAction(),
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 { onBeforeUnmount, provide, ref, ShallowRef, shallowRef, watch } from "vue";
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
- variant: { type: String, default () { return 'elevated' } },
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 'Write something here...' } },
24
- showCharacterCount: { type: Boolean, default () { return true } },
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
- const theme = ref(useTheme().current.value.dark ? 'dark' : 'light')
38
+ // console.log('override extension components', computed(() => props.overrideExtensionsComponents).value)
31
39
 
32
- const editor: ShallowRef<Editor | undefined> = shallowRef(
33
- getTiptapEditor(props.content, props.placeholder, props.slashCommands)
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="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="variant" :theme="theme" />
90
+ <MenuFloating v-if="floatingMenu" :variant="variantBtn" :theme="currentTheme" />
76
91
 
77
- <MenuBubble v-if="bubbleMenu" :variant="variant" :theme="theme" />
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
- <template v-if="showCharacterCount">
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
- <VBtn v-else v-bind="toolbarItem.props" v-on="toolbarItem.attrs" size="32">
30
- <VTooltip :text="ucFirst(t(toolbarItem.tooltip))" location="top" activator="parent" />
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
- <VIcon v-if="toolbarItem.icon" :icon="toolbarItem.icon" size="small" />
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 LinkDialog from "@tiptapify/extensions/components/LinkDialog.vue";
4
- import ShowSourceDialog from "@tiptapify/extensions/components/ShowSourceDialog.vue";
5
- import PreviewDialog from "@tiptapify/extensions/components/PreviewDialog.vue";
6
- import Group from "@tiptapify/components/Toolbar/Group.vue";
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, ToolbarItemSections } from "@tiptapify/components/Toolbar/items";
8
+ import { toolbarItems } from "@tiptapify/components/Toolbar/items";
9
+ import { useTheme } from "vuetify/framework";
12
10
 
13
11
  const props = defineProps({
14
- variant: { type: String, default () { return 'flat' }},
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 toolbarLinkButton = ref(null)
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
- const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
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
- <VToolbarItems class="py-2">
46
- <template v-for="(toolbarSection, sectionKey) in toolbarItemsRef" :key="sectionKey">
47
- <Group v-if="toolbarSection.group" :variant="variant" :toolbar-section="toolbarSection" />
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
- <div class="menu-divider"></div>
72
- </template>
73
- </VToolbarItems>
54
+ <Items v-else :items="items" />
74
55
  </VToolbar>
75
56
 
76
- <LinkDialog ref="toolbarLinkButton" />
77
- <PreviewDialog />
78
- <ShowSourceDialog />
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>