tiptapify 0.0.6 → 0.0.8

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 (42) hide show
  1. package/README.md +4 -2
  2. package/dist/tiptapify.css +1 -1
  3. package/dist/{tiptapify.es.js → tiptapify.mjs} +54979 -52123
  4. package/dist/tiptapify.umd.js +41 -43
  5. package/package.json +8 -8
  6. package/src/components/Footer.vue +5 -6
  7. package/src/components/MenuBubble.vue +41 -39
  8. package/src/components/MenuFloating.vue +10 -10
  9. package/src/components/Tiptapify.vue +95 -14
  10. package/src/components/Toolbar/Group.vue +8 -43
  11. package/src/components/Toolbar/GroupDropdown.vue +85 -0
  12. package/src/components/Toolbar/Index.vue +20 -19
  13. package/src/components/Toolbar/items/media.ts +195 -9
  14. package/src/components/Toolbar/items/misc.ts +10 -1
  15. package/src/components/Toolbar/items/style.ts +2 -2
  16. package/src/components/Toolbar/items.ts +3 -3
  17. package/src/components/editorExtensions.ts +11 -7
  18. package/src/components/index.ts +13 -0
  19. package/src/extensions/components/ImageDialog.vue +142 -0
  20. package/src/extensions/components/LinkDialog.vue +142 -0
  21. package/src/extensions/components/PreviewDialog.vue +44 -0
  22. package/src/{components/extensions/components/ShowSource.vue → extensions/components/ShowSourceDialog.vue} +16 -10
  23. package/src/extensions/components/TableBuilder.vue +138 -0
  24. package/src/extensions/image.ts +31 -0
  25. package/src/extensions/link.ts +24 -0
  26. package/src/extensions/preview.ts +40 -0
  27. package/src/{components/extensions → extensions}/view-source.ts +1 -6
  28. package/src/i18n/locales/de.json +78 -45
  29. package/src/i18n/locales/en.json +37 -4
  30. package/src/i18n/locales/es.json +46 -13
  31. package/src/i18n/locales/fr.json +54 -21
  32. package/src/i18n/locales/it.json +51 -18
  33. package/src/i18n/locales/pl.json +45 -12
  34. package/src/i18n/locales/ru.json +36 -3
  35. package/src/i18n/locales/ua.json +36 -3
  36. package/src/index.ts +0 -1
  37. package/src/utils/helpers.ts +17 -0
  38. package/src/components/extensions/components/LinkDialog.vue +0 -98
  39. package/src/composable/useEditor.ts +0 -35
  40. /package/src/{components/extensions → extensions}/components/slashCommands/CommandsList.vue +0 -0
  41. /package/src/{components/extensions → extensions}/components/slashCommands/suggestion.ts +0 -0
  42. /package/src/{components/extensions → extensions}/slash-commands.ts +0 -0
@@ -1,16 +1,19 @@
1
1
  <script setup lang="ts">
2
- import LinkDialog from "@tiptapify/components/extensions/components/LinkDialog.vue";
3
- import ShowSource from "@tiptapify/components/extensions/components/ShowSource.vue";
2
+ import { Editor } from "@tiptap/vue-3";
3
+ import ImageDialog from "@tiptapify/extensions/components/ImageDialog.vue";
4
+ import LinkDialog from "@tiptapify/extensions/components/LinkDialog.vue";
5
+ import ShowSourceDialog from "@tiptapify/extensions/components/ShowSourceDialog.vue";
6
+ import PreviewDialog from "@tiptapify/extensions/components/PreviewDialog.vue";
4
7
  import Group from "@tiptapify/components/Toolbar/Group.vue";
5
8
  import Toggle from "@tiptapify/components/Toolbar/Toggle.vue";
6
- import { useEditor } from "@tiptapify/composable/useEditor";
7
- import { computed, defineProps, Ref, ref } from 'vue'
9
+ import { computed, defineProps, inject, Ref, ref } from 'vue'
8
10
  import { useI18n } from "vue-i18n";
9
11
 
10
12
  import { toolbarItems, ToolbarItemSections } from "@tiptapify/components/Toolbar/items";
11
13
 
12
14
  const props = defineProps({
13
- variant: { type: String, default () { return 'flat' }},
15
+ variantBtn: { type: String, default () { return 'elevated' }},
16
+ variantField: { type: String, default () { return 'solo' }},
14
17
  items: { type: Array<string>, default() { return [] }},
15
18
  itemsExclude: { type: Boolean, default() { return false } },
16
19
  headingLevels: { type: Array<number>, default() { return [] }},
@@ -23,17 +26,13 @@ const props = defineProps({
23
26
 
24
27
  const { t } = useI18n();
25
28
 
26
- const { editor } = useEditor()
27
- const editorInstance = ref(editor.getInstance())
28
-
29
- const toolbarLinkButton = ref(null)
29
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
30
30
 
31
31
  const items = toolbarItems(
32
- editorInstance,
32
+ editor,
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
  const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
39
38
 
@@ -44,15 +43,15 @@ const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
44
43
  <VToolbar elevation="1" :theme="theme" height="auto" :class="`ps-1 rounded-t-${rounded}`">
45
44
  <VToolbarItems class="py-2">
46
45
  <template v-for="(toolbarSection, sectionKey) in toolbarItemsRef" :key="sectionKey">
47
- <Group v-if="toolbarSection.group" :variant="variant" :toolbar-section="toolbarSection" />
46
+ <Group v-if="toolbarSection.group" :variant="variantBtn" :toolbar-section="toolbarSection" />
48
47
 
49
- <Toggle v-else-if="toolbarSection.toggle" :variant="variant" :toolbar-section="toolbarSection" />
48
+ <Toggle v-else-if="toolbarSection.toggle" :variant="variantBtn" :toolbar-section="toolbarSection" />
50
49
 
51
50
  <VBtn
52
51
  v-else
53
52
  v-for="(toolbarItem, itemKey) in toolbarSection.items"
54
53
  :key="itemKey"
55
- :variant="variant"
54
+ :variant="variantBtn"
56
55
  v-bind="toolbarItem.props"
57
56
  v-on="toolbarItem.attrs"
58
57
  class="menu-button"
@@ -64,8 +63,8 @@ const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
64
63
 
65
64
  <VIcon v-if="toolbarItem.icon" :icon="toolbarItem.icon" size="16" />
66
65
  <span v-else class="menu-item-title">
67
- {{ t(toolbarItem.name) }}
68
- </span>
66
+ {{ t(toolbarItem.name) }}
67
+ </span>
69
68
  </VBtn>
70
69
 
71
70
  <div class="menu-divider"></div>
@@ -73,8 +72,10 @@ const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
73
72
  </VToolbarItems>
74
73
  </VToolbar>
75
74
 
76
- <LinkDialog ref="toolbarLinkButton" />
77
- <ShowSource />
75
+ <ImageDialog :variant-field="variantField" />
76
+ <LinkDialog :variant-field="variantField" />
77
+ <PreviewDialog />
78
+ <ShowSourceDialog :variant-field="variantField" />
78
79
  </div>
79
80
  </template>
80
81
 
@@ -1,12 +1,13 @@
1
1
  import * as mdi from "@mdi/js";
2
2
  import { Editor } from "@tiptap/vue-3";
3
- import { computed } from "vue";
3
+ import TableBuilder from "@tiptapify/extensions/components/TableBuilder.vue";
4
+ import { computed, markRaw, Ref } from "vue";
4
5
 
5
- export function getMediaItems(editor: Editor, toolbarLinkButton: any) {
6
+ export function getMediaItems(editor: Editor) {
6
7
  return {
7
8
  link: {
8
- name: 'format.link',
9
- tooltip: 'format.link',
9
+ name: 'media.link',
10
+ tooltip: 'media.link',
10
11
  icon: computed(() => editor.isActive('link') ? mdi.mdiLinkOff : mdi.mdiLink),
11
12
  enabled: true,
12
13
  props: {
@@ -14,12 +15,197 @@ export function getMediaItems(editor: Editor, toolbarLinkButton: any) {
14
15
  disabled: computed(() => editor.isActive('code') || editor.isActive('codeBlock')),
15
16
  },
16
17
  attrs: {
17
- click: computed(() => {
18
- return editor.isActive('link')
19
- ? editor.chain().focus().unsetLink().run
20
- : toolbarLinkButton?.value?.open
21
- })
18
+ click: () => editor.commands.showLink()
22
19
  }
20
+ },
21
+ image: {
22
+ name: 'media.image',
23
+ tooltip: 'media.image',
24
+ icon: mdi.mdiImage,
25
+ enabled: true,
26
+ props: {
27
+ color: computed(() => editor.isActive('image') ? 'primary' : ''),
28
+ disabled: computed(() => editor.isActive('code') || editor.isActive('codeBlock')),
29
+ },
30
+ attrs: {
31
+ click: () => editor.commands.showTiptapifyImage()
32
+ }
33
+ },
34
+ table: {
35
+ name: 'tables',
36
+ tooltip: 'media.tables.table',
37
+ icon: mdi.mdiTable,
38
+ modelValue: false,
39
+ enabled: true,
40
+ props: {
41
+ color: computed(() => editor.isActive('table') ? 'primary' : ''),
42
+ disabled: computed(() => !editor.can().chain().focus().insertTable().run()),
43
+ },
44
+ children: [
45
+ {
46
+ name: 'insert table',
47
+ tooltip: 'media.tables.insertTable',
48
+ icon: mdi.mdiTablePlus,
49
+ enabled: true,
50
+ props: {
51
+ disabled: computed(() => !editor.can().chain().focus().insertTable().run()),
52
+ activator: 'parent',
53
+ openOnClick: true,
54
+ openOnHover: false,
55
+ closeOnContentClick: false,
56
+ submenu: true
57
+ },
58
+ component: markRaw(TableBuilder),
59
+ },
60
+ {
61
+ name: 'delete table',
62
+ tooltip: 'media.tables.deleteTable',
63
+ icon: mdi.mdiTableMinus,
64
+ enabled: true,
65
+ props: {
66
+ disabled: computed(() => !editor.can().chain().focus().deleteTable().run()),
67
+ },
68
+ attrs: {
69
+ click: () => editor.chain().focus().deleteTable().run()
70
+ }
71
+ },
72
+ {
73
+ name: 'table row',
74
+ tooltip: 'media.tables.row',
75
+ icon: mdi.mdiTableRow,
76
+ enabled: true,
77
+ props: {
78
+ disabled: computed(
79
+ () =>
80
+ !editor.can().chain().focus().addRowBefore().run() &&
81
+ !editor.can().chain().focus().addRowAfter().run() &&
82
+ !editor.can().chain().focus().deleteRow().run()
83
+ ),
84
+ openOnHover: true,
85
+ openOnClick: true,
86
+ activator: 'parent',
87
+ submenu: true
88
+ },
89
+ children: [
90
+ {
91
+ name: 'insert row before',
92
+ tooltip: 'media.tables.insertRowBefore',
93
+ icon: mdi.mdiTableRowPlusBefore,
94
+ enabled: true,
95
+ props: {
96
+ disabled: computed(() => !editor.can().chain().focus().addRowBefore().run()),
97
+ },
98
+ attrs: {
99
+ click: () => editor.chain().focus().addRowBefore().run()
100
+ }
101
+ },
102
+ {
103
+ name: 'insert row after',
104
+ tooltip: 'media.tables.insertRowAfter',
105
+ icon: mdi.mdiTableRowPlusAfter,
106
+ enabled: true,
107
+ props: {
108
+ disabled: computed(() => !editor.can().chain().focus().addRowAfter().run()),
109
+ },
110
+ attrs: {
111
+ click: () => editor.chain().focus().addRowAfter().run()
112
+ }
113
+ },
114
+ {
115
+ name: 'delete row',
116
+ tooltip: 'media.tables.deleteRow',
117
+ icon: mdi.mdiTableRowRemove,
118
+ enabled: true,
119
+ props: {
120
+ disabled: computed(() => !editor.can().chain().focus().deleteRow().run()),
121
+ },
122
+ attrs: {
123
+ click: () => editor.chain().focus().deleteRow().run()
124
+ }
125
+ },
126
+ ]
127
+ },
128
+ {
129
+ name: 'column',
130
+ tooltip: 'media.tables.col',
131
+ icon: mdi.mdiTableColumn,
132
+ enabled: true,
133
+ props: {
134
+ disabled: computed(
135
+ () =>
136
+ !editor.can().chain().focus().addColumnBefore().run() &&
137
+ !editor.can().chain().focus().addColumnAfter().run() &&
138
+ !editor.can().chain().focus().deleteColumn().run()
139
+ ),
140
+ openOnHover: true,
141
+ openOnClick: true,
142
+ activator: 'parent',
143
+ submenu: true
144
+ },
145
+ children: [
146
+ {
147
+ name: 'insert col before',
148
+ tooltip: 'media.tables.insertColBefore',
149
+ icon: mdi.mdiTableColumnPlusBefore,
150
+ enabled: true,
151
+ props: {
152
+ disabled: computed(() => !editor.can().chain().focus().addColumnBefore().run()),
153
+ },
154
+ attrs: {
155
+ click: () => editor.chain().focus().addColumnBefore().run()
156
+ }
157
+ },
158
+ {
159
+ name: 'insert column after',
160
+ tooltip: 'media.tables.insertColAfter',
161
+ icon: mdi.mdiTableColumnPlusAfter,
162
+ enabled: true,
163
+ props: {
164
+ disabled: computed(() => !editor.can().chain().focus().addColumnAfter().run()),
165
+ },
166
+ attrs: {
167
+ click: () => editor.chain().focus().addColumnAfter().run()
168
+ }
169
+ },
170
+ {
171
+ name: 'delete column',
172
+ tooltip: 'media.tables.deleteCol',
173
+ icon: mdi.mdiTableColumnRemove,
174
+ enabled: true,
175
+ props: {
176
+ disabled: computed(() => !editor.can().chain().focus().deleteColumn().run()),
177
+ },
178
+ attrs: {
179
+ click: () => editor.chain().focus().deleteColumn().run()
180
+ }
181
+ }
182
+ ]
183
+ },
184
+ {
185
+ name: 'merge cells',
186
+ tooltip: 'media.tables.mergeCells',
187
+ icon: mdi.mdiTableMergeCells,
188
+ enabled: true,
189
+ props: {
190
+ disabled: computed(() => !editor.can().chain().focus().mergeCells().run()),
191
+ },
192
+ attrs: {
193
+ click: () => editor.chain().focus().mergeCells().run()
194
+ }
195
+ },
196
+ {
197
+ name: 'split cell',
198
+ tooltip: 'media.tables.splitCell',
199
+ icon: mdi.mdiTableSplitCell,
200
+ enabled: true,
201
+ props: {
202
+ disabled: computed(() => !editor.can().chain().focus().splitCell().run()),
203
+ },
204
+ attrs: {
205
+ click: () => editor.chain().focus().splitCell().run()
206
+ }
207
+ }
208
+ ]
23
209
  }
24
210
  }
25
211
  }
@@ -1,6 +1,6 @@
1
1
  import * as mdi from "@mdi/js";
2
2
  import { Editor } from "@tiptap/vue-3";
3
- import { computed } from "vue";
3
+ import { computed, Ref } from "vue";
4
4
 
5
5
  export function getMiscItems(editor: Editor) {
6
6
  return {
@@ -34,6 +34,15 @@ export function getMiscItems(editor: Editor) {
34
34
  click: () => editor.commands.showSource()
35
35
  }
36
36
  },
37
+ preview: {
38
+ name: 'preview',
39
+ tooltip: 'misc.preview',
40
+ icon: mdi.mdiFileEyeOutline,
41
+ enabled: true,
42
+ attrs: {
43
+ click: () => editor.commands.showPreview()
44
+ }
45
+ },
37
46
  formatClear: {
38
47
  name: 'format clear',
39
48
  tooltip: 'format.formatClear',
@@ -70,7 +70,7 @@ export function getStyleItems(editor: Editor, fontMeasure: string, customHeading
70
70
  name: 'font-family',
71
71
  tooltip: 'style.fontFamily',
72
72
  icon: mdi.mdiFormatFont,
73
- modelValue: null,
73
+ modelValue: false,
74
74
  enabled: true,
75
75
  attrs: {
76
76
  click: () => editor.chain().focus().unsetFontFamily().run()
@@ -96,7 +96,7 @@ export function getStyleItems(editor: Editor, fontMeasure: string, customHeading
96
96
  name: 'font-size',
97
97
  tooltip: 'style.fontSize',
98
98
  icon: mdi.mdiFormatSize,
99
- modelValue: computed(() => editor.getAttributes('textStyle').fontSize || null),
99
+ modelValue: false,
100
100
  enabled: true,
101
101
  attrs: {
102
102
  click: () => editor.chain().focus().unsetFontSize().run()
@@ -22,6 +22,7 @@ export interface ToolbarItem {
22
22
  icon: string|ComputedRef<string>,
23
23
  noI18n?: boolean,
24
24
  enabled: boolean,
25
+ component?: any,
25
26
  modelValue?: any,
26
27
  group?: boolean,
27
28
  toggle?: boolean,
@@ -48,8 +49,7 @@ export function toolbarItems(
48
49
  editor: any,
49
50
  fontMeasure: string,
50
51
  items: { list: Array<string>, exclude: boolean },
51
- customHeadingLevels: Array<number>,
52
- toolbarLinkButton: Ref,
52
+ customHeadingLevels: Array<number>
53
53
  ): ToolbarItemSections {
54
54
  const styleItems = ref(getStyleItems(editor.value, fontMeasure, customHeadingLevels))
55
55
  const formatItems = ref(getFormatItems(editor.value))
@@ -58,7 +58,7 @@ export function toolbarItems(
58
58
  const listItems = ref(getListItems(editor.value))
59
59
  const actionsItems = ref(getActionsItems(editor.value))
60
60
  const miscItems = ref(getMiscItems(editor.value))
61
- const mediaItems = ref(getMediaItems(editor.value, toolbarLinkButton))
61
+ const mediaItems = ref(getMediaItems(editor.value))
62
62
 
63
63
  const allMenuItems: ToolbarItemSections = {
64
64
  /**
@@ -23,11 +23,13 @@ import { Underline } from '@tiptap/extension-underline'
23
23
  import { TableKit } from '@tiptap/extension-table'
24
24
  import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
25
25
 
26
- import { Link } from '@tiptap/extension-link'
26
+ import { TiptapifyLink } from '@tiptapify/extensions/link'
27
+ import { TiptapifyImage } from '@tiptapify/extensions/image'
27
28
  import CodeBlockComponent from '@tiptapify/components/CodeBlockComponent.vue'
28
- import { ViewSource } from '@tiptapify/components/extensions/view-source'
29
- import SlashCommands from '@tiptapify/components/extensions/slash-commands'
30
- import suggestion from '@tiptapify/components/extensions/components/slashCommands/suggestion'
29
+ import { ViewSource } from '@tiptapify/extensions/view-source'
30
+ import { Preview } from '@tiptapify/extensions/preview'
31
+ import SlashCommands from '@tiptapify/extensions/slash-commands'
32
+ import suggestion from '@tiptapify/extensions/components/slashCommands/suggestion'
31
33
 
32
34
  // load all languages with "all" or common languages with "common"
33
35
  import { common, createLowlight } from 'lowlight'
@@ -68,11 +70,12 @@ export function editorExtensions (placeholder: string, slashCommands: boolean) {
68
70
  Typography,
69
71
  Underline,
70
72
  Highlight,
71
- Link.configure({
73
+ TiptapifyLink.configure({
72
74
  openOnClick: false,
73
- defaultProtocol: 'https',
75
+ defaultProtocol: 'https'
74
76
  }),
75
77
  Image,
78
+ TiptapifyImage,
76
79
  Superscript,
77
80
  Subscript,
78
81
  TableKit,
@@ -94,7 +97,8 @@ export function editorExtensions (placeholder: string, slashCommands: boolean) {
94
97
  }),
95
98
  Placeholder.configure({ placeholder }),
96
99
  CharacterCount,
97
- ViewSource
100
+ ViewSource,
101
+ Preview
98
102
  ]
99
103
 
100
104
  if (slashCommands) {
@@ -0,0 +1,13 @@
1
+ import { Editor, useEditor } from "@tiptap/vue-3";
2
+ import { editorExtensions } from "@tiptapify/components/editorExtensions";
3
+ import { ShallowRef } from "vue";
4
+
5
+ export function getTiptapEditor (content: any, placeholder: string, slashCommands: boolean = true): ShallowRef<Editor | undefined> {
6
+ const extensions = editorExtensions(placeholder, slashCommands)
7
+ const editor: ShallowRef<Editor | undefined> = useEditor({
8
+ content,
9
+ extensions,
10
+ })
11
+
12
+ return editor
13
+ }
@@ -0,0 +1,142 @@
1
+ <script setup lang="ts">
2
+
3
+ import * as mdi from '@mdi/js'
4
+ import { Editor } from "@tiptap/vue-3";
5
+
6
+ import { useI18n } from 'vue-i18n'
7
+ import { computed, inject, onMounted, onUnmounted, Ref, ref } from 'vue'
8
+
9
+ import helpers from '@tiptapify/utils/helpers'
10
+
11
+ defineProps({
12
+ variantBtn: { type: String, default() { return 'elevated' }},
13
+ variantField: { type: String, default() { return 'solo' }}
14
+ })
15
+
16
+ const { ucFirst } = helpers
17
+
18
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
19
+ const { t } = useI18n()
20
+
21
+ const generateImageAttrs = () => ({
22
+ src: '',
23
+ alt: '',
24
+ height: null,
25
+ width: null
26
+ })
27
+
28
+ const attrs = ref(generateImageAttrs())
29
+
30
+ const dialog = ref<boolean>(false)
31
+
32
+ const isDisabled = computed(() => {
33
+ const { src } = attrs.value
34
+ return !src
35
+ })
36
+
37
+ function apply() {
38
+ let { src, alt, width, height } = attrs.value
39
+
40
+ const imageOptions: { src: string, alt: string, width?: number, height?: number} = {
41
+ src,
42
+ alt
43
+ }
44
+
45
+ if (width) {
46
+ imageOptions.width = width
47
+ }
48
+
49
+ if (height) {
50
+ imageOptions.height = height
51
+ }
52
+
53
+ if (src) {
54
+ editor.value.chain().focus().setImage(imageOptions).run()
55
+ }
56
+
57
+ close()
58
+ }
59
+
60
+ function clear() {
61
+ editor.value.commands.deleteSelection()
62
+
63
+ close()
64
+ }
65
+
66
+ function close() {
67
+ dialog.value = false
68
+
69
+ attrs.value = generateImageAttrs()
70
+ }
71
+
72
+ const showTiptapifyImage = (event: CustomEvent) => {
73
+ attrs.value.src = event.detail.image?.src
74
+ attrs.value.alt = event.detail.image?.alt
75
+ attrs.value.width = event.detail.image?.width
76
+ attrs.value.height = event.detail.image?.height
77
+
78
+ dialog.value = true;
79
+ }
80
+
81
+ onMounted(() => {
82
+ window.addEventListener('tiptapify-show-tiptapifyImage', showTiptapifyImage as EventListener)
83
+ })
84
+
85
+ onUnmounted(() => {
86
+ window.removeEventListener('tiptapify-show-tiptapifyImage', showTiptapifyImage as EventListener)
87
+ })
88
+ </script>
89
+
90
+ <template>
91
+ <VDialog v-model="dialog" max-width="800" absolute @click:outside="close">
92
+ <VCard>
93
+ <VToolbar class="px-6" density="compact">
94
+ <span class="headline">{{ ucFirst(t('dialog.image.title')) }}</span>
95
+
96
+ <VSpacer />
97
+
98
+ <VBtn class="mx-0" icon @click="close">
99
+ <VIcon :icon="mdi.mdiClose" />
100
+ </VBtn>
101
+ </VToolbar>
102
+
103
+ <VCardText>
104
+ <VRow>
105
+ <VCol cols="12">
106
+ <VTextField v-model="attrs.src" :variant="variantField" :label="ucFirst(t('dialog.image.src'))" />
107
+ </VCol>
108
+
109
+ <VCol cols="12" md="6">
110
+ <VTextField v-model="attrs.alt" :variant="variantField" :label="ucFirst(t('dialog.image.alt'))" />
111
+ </VCol>
112
+
113
+ <VCol cols="12" md="3">
114
+ <VTextField v-model="attrs.width" type="number" :variant="variantField" :precision="0" :min="1" :label="ucFirst(t('dialog.image.width'))" />
115
+ </VCol>
116
+
117
+ <VCol cols="12" md="3">
118
+ <VTextField v-model="attrs.height" type="number" :variant="variantField" :precision="0" :min="1" :label="ucFirst(t('dialog.image.height'))" />
119
+ </VCol>
120
+ </VRow>
121
+ </VCardText>
122
+
123
+ <VCardActions>
124
+ <VRow>
125
+ <VCol class="d-flex justify-start">
126
+ <VBtn color="warning" v-if="editor.isActive('image')" :variant="variantBtn" :disabled="isDisabled" @click="clear">
127
+ {{ ucFirst(t('dialog.clear')) }}
128
+ </VBtn>
129
+ </VCol>
130
+ <VCol class="d-flex justify-end">
131
+ <VBtn :variant="variantBtn" @click="close" class="mr-2">
132
+ {{ ucFirst(t('dialog.close')) }}
133
+ </VBtn>
134
+ <VBtn color="primary" :variant="variantBtn" :disabled="isDisabled" @click="apply">
135
+ {{ ucFirst(t('dialog.apply')) }}
136
+ </VBtn>
137
+ </VCol>
138
+ </VRow>
139
+ </VCardActions>
140
+ </VCard>
141
+ </VDialog>
142
+ </template>