tiptapify 0.0.9 → 0.0.11

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 (52) hide show
  1. package/README.md +1 -1
  2. package/dist/tiptapify.css +1 -1
  3. package/dist/tiptapify.mjs +21133 -19765
  4. package/dist/tiptapify.umd.js +37 -38
  5. package/package.json +43 -43
  6. package/src/components/Tiptapify.vue +2 -2
  7. package/src/components/Toolbar/GroupBtn.vue +2 -6
  8. package/src/components/Toolbar/GroupDropdown.vue +4 -8
  9. package/src/components/Toolbar/Index.vue +5 -4
  10. package/src/components/Toolbar/Items.vue +2 -2
  11. package/src/components/Toolbar/Toggle.vue +2 -2
  12. package/src/components/Toolbar/items.ts +20 -20
  13. package/src/components/UI/Dialog.vue +141 -0
  14. package/src/components/editorExtensions.ts +1 -1
  15. package/src/{components/Toolbar/items/actions.ts → composables/Toolbar/useActionsItems.ts} +6 -3
  16. package/src/{components/Toolbar/items/alignment.ts → composables/Toolbar/useAlignmentItems.ts} +12 -9
  17. package/src/{components/Toolbar/items/formatExtra.ts → composables/Toolbar/useFormatExtraItems.ts} +10 -7
  18. package/src/{components/Toolbar/items/format.ts → composables/Toolbar/useFormatItems.ts} +20 -17
  19. package/src/{components/Toolbar/items/list.ts → composables/Toolbar/useListItems.ts} +14 -11
  20. package/src/{components/Toolbar/items/media.ts → composables/Toolbar/useMediaItems.ts} +22 -20
  21. package/src/{components/Toolbar/items/misc.ts → composables/Toolbar/useMiscItems.ts} +9 -6
  22. package/src/composables/Toolbar/useStyleItems.ts +231 -0
  23. package/src/{components/Toolbar/fonts.ts → constants/style.ts} +21 -0
  24. package/src/extensions/components/FontFamily.vue +82 -0
  25. package/src/extensions/components/FontSize.vue +83 -0
  26. package/src/extensions/components/ImageDialog.vue +17 -29
  27. package/src/extensions/components/LineHeight.vue +82 -0
  28. package/src/extensions/components/LinkDialog.vue +75 -44
  29. package/src/extensions/components/PreviewDialog.vue +8 -16
  30. package/src/extensions/components/ShowSourceDialog.vue +17 -18
  31. package/src/extensions/components/StyleColor.vue +68 -15
  32. package/src/extensions/components/TableBuilder.vue +3 -6
  33. package/src/extensions/link.ts +8 -0
  34. package/src/extensions/slash-commands.ts +1 -1
  35. package/src/i18n/index.ts +0 -1
  36. package/src/i18n/locales/ch.json +119 -0
  37. package/src/i18n/locales/cz.json +119 -0
  38. package/src/i18n/locales/de.json +83 -76
  39. package/src/i18n/locales/en.json +82 -75
  40. package/src/i18n/locales/es.json +80 -73
  41. package/src/i18n/locales/fr.json +81 -73
  42. package/src/i18n/locales/it.json +80 -73
  43. package/src/i18n/locales/la.json +119 -0
  44. package/src/i18n/locales/lt.json +119 -0
  45. package/src/i18n/locales/nl.json +119 -0
  46. package/src/i18n/locales/pl.json +80 -74
  47. package/src/i18n/locales/pt.json +119 -0
  48. package/src/i18n/locales/ru.json +78 -71
  49. package/src/i18n/locales/se.json +119 -0
  50. package/src/i18n/locales/ua.json +79 -72
  51. package/src/components/Toolbar/items/style.ts +0 -187
  52. /package/src/{components → extensions/components}/CodeBlockComponent.vue +0 -0
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts">
2
+
3
+ import { Editor } from "@tiptap/vue-3";
4
+
5
+ import { computed, inject, Ref, ref } from 'vue'
6
+ import { useI18n } from "vue-i18n";
7
+
8
+ const props = defineProps({
9
+ sizes: { type: Array<number>, default: [] },
10
+ measure: { type: String, default: 'px' },
11
+ fontSize: { type: Number, default () { return null } },
12
+ })
13
+
14
+ const { t } = useI18n();
15
+
16
+ const emit = defineEmits(['close'])
17
+
18
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
19
+
20
+ const initialFontSize = ref(computed(() => props.fontSize).value)
21
+
22
+ const fontSizeSelected = ref<boolean>(false)
23
+
24
+ function hoverFontSize(fontSize: number) {
25
+ fontSizeSelected.value = false
26
+
27
+ editor.value.chain().focus().setFontSize(`${fontSize}${props.measure}`).run()
28
+ }
29
+
30
+ function resetFontSize() {
31
+ if (fontSizeSelected.value) {
32
+ return
33
+ }
34
+
35
+ initialFontSize.value
36
+ ? editor.value.chain().focus().setFontSize(`${initialFontSize.value}${props.measure}`).run()
37
+ : editor.value.chain().focus().unsetFontSize().run()
38
+ }
39
+
40
+ function clearFontSize() {
41
+ editor.value.chain().focus().unsetFontSize().run()
42
+ emit('close')
43
+ }
44
+
45
+ function setFontSize() {
46
+ fontSizeSelected.value = true
47
+
48
+ emit('close')
49
+ }
50
+
51
+ function isFontSizeActive(fontSize: number): boolean {
52
+ return editor.value.isActive('textStyle', { fontSize: `${fontSize}${props.measure}` }) || fontSize === initialFontSize.value
53
+ }
54
+ </script>
55
+
56
+ <template>
57
+ <VList class="tiptapify-font-size-list">
58
+ <VListItem :disabled="fontSize === null" density="compact" @click="clearFontSize">
59
+ <VListItemTitle class="font-italic text-grey-darken-1">
60
+ {{ t('defaultValue') }}
61
+ </VListItemTitle>
62
+ </VListItem>
63
+ <VListItem
64
+ v-for="size in sizes"
65
+ :active="isFontSizeActive(size)"
66
+ :color="size === initialFontSize ? 'primary' : ''"
67
+ density="compact"
68
+ @click="setFontSize"
69
+ @mouseover="hoverFontSize(size)"
70
+ @mouseleave="resetFontSize"
71
+ >
72
+ <VListItemTitle>
73
+ {{ size }}{{ measure}}
74
+ </VListItemTitle>
75
+ </VListItem>
76
+ </VList>
77
+ </template>
78
+
79
+ <style lang="scss" scoped>
80
+ .tiptapify-font-size-list {
81
+ max-height: 390px;
82
+ }
83
+ </style>
@@ -1,20 +1,16 @@
1
1
  <script setup lang="ts">
2
2
 
3
- import * as mdi from '@mdi/js'
4
3
  import { Editor } from "@tiptap/vue-3";
4
+ import Dialog from "@tiptapify/components/UI/Dialog.vue";
5
5
 
6
6
  import { useI18n } from 'vue-i18n'
7
7
  import { computed, inject, onMounted, onUnmounted, Ref, ref } from 'vue'
8
8
 
9
- import helpers from '@tiptapify/utils/helpers'
10
-
11
9
  defineProps({
12
10
  variantBtn: { type: String, default() { return 'elevated' }},
13
11
  variantField: { type: String, default() { return 'solo' }}
14
12
  })
15
13
 
16
- const { ucFirst } = helpers
17
-
18
14
  const editor = inject('tiptapifyEditor') as Ref<Editor>
19
15
  const { t } = useI18n()
20
16
 
@@ -27,7 +23,7 @@ const generateImageAttrs = () => ({
27
23
 
28
24
  const attrs = ref(generateImageAttrs())
29
25
 
30
- const dialog = ref<boolean>(false)
26
+ const dialog = ref(null)
31
27
 
32
28
  const isDisabled = computed(() => {
33
29
  const { src } = attrs.value
@@ -64,7 +60,7 @@ function clear() {
64
60
  }
65
61
 
66
62
  function close() {
67
- dialog.value = false
63
+ dialog.value.close()
68
64
 
69
65
  attrs.value = generateImageAttrs()
70
66
  }
@@ -75,7 +71,7 @@ const showTiptapifyImage = (event: CustomEvent) => {
75
71
  attrs.value.width = event.detail.image?.width
76
72
  attrs.value.height = event.detail.image?.height
77
73
 
78
- dialog.value = true;
74
+ dialog.value.open()
79
75
  }
80
76
 
81
77
  onMounted(() => {
@@ -88,55 +84,47 @@ onUnmounted(() => {
88
84
  </script>
89
85
 
90
86
  <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
-
87
+ <Dialog ref="dialog" module="image" :max-width="800">
88
+ <template #content>
103
89
  <VCardText>
104
90
  <VRow>
105
91
  <VCol cols="12">
106
- <VTextField v-model="attrs.src" :variant="variantField" :label="ucFirst(t('dialog.image.src'))" />
92
+ <VTextField v-model="attrs.src" density="compact" variant="outlined" :label="t('dialog.image.src')" />
107
93
  </VCol>
108
94
 
109
95
  <VCol cols="12" md="6">
110
- <VTextField v-model="attrs.alt" :variant="variantField" :label="ucFirst(t('dialog.image.alt'))" />
96
+ <VTextField v-model="attrs.alt" density="compact" variant="outlined" :label="t('dialog.image.alt')" />
111
97
  </VCol>
112
98
 
113
99
  <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'))" />
100
+ <VTextField v-model="attrs.width" type="number" density="compact" variant="outlined" :precision="0" :min="1" :label="t('dialog.image.width')" />
115
101
  </VCol>
116
102
 
117
103
  <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'))" />
104
+ <VTextField v-model="attrs.height" type="number" density="compact" variant="outlined" :precision="0" :min="1" :label="t('dialog.image.height')" />
119
105
  </VCol>
120
106
  </VRow>
121
107
  </VCardText>
108
+ </template>
122
109
 
110
+ <template #actions>
123
111
  <VCardActions>
124
112
  <VRow>
125
113
  <VCol class="d-flex justify-start">
126
114
  <VBtn color="warning" v-if="editor.isActive('image')" :variant="variantBtn" :disabled="isDisabled" @click="clear">
127
- {{ ucFirst(t('dialog.clear')) }}
115
+ {{ t('dialog.clear') }}
128
116
  </VBtn>
129
117
  </VCol>
130
118
  <VCol class="d-flex justify-end">
131
119
  <VBtn :variant="variantBtn" @click="close" class="mr-2">
132
- {{ ucFirst(t('dialog.close')) }}
120
+ {{ t('dialog.close') }}
133
121
  </VBtn>
134
122
  <VBtn color="primary" :variant="variantBtn" :disabled="isDisabled" @click="apply">
135
- {{ ucFirst(t('dialog.apply')) }}
123
+ {{ t('dialog.apply') }}
136
124
  </VBtn>
137
125
  </VCol>
138
126
  </VRow>
139
127
  </VCardActions>
140
- </VCard>
141
- </VDialog>
128
+ </template>
129
+ </Dialog>
142
130
  </template>
@@ -0,0 +1,82 @@
1
+ <script setup lang="ts">
2
+
3
+ import { Editor } from "@tiptap/vue-3";
4
+
5
+ import { computed, inject, Ref, ref } from 'vue'
6
+ import { useI18n } from "vue-i18n";
7
+
8
+ const props = defineProps({
9
+ lineHeights: { type: Array<number>, default () { return [] } },
10
+ lineHeight: { type: Number, default () { return null } },
11
+ })
12
+
13
+ const { t } = useI18n();
14
+
15
+ const emit = defineEmits(['close'])
16
+
17
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
18
+
19
+ const initialLineHeight = ref(computed(() => props.lineHeight).value)
20
+
21
+ const lineHeightSelected = ref<boolean>(false)
22
+
23
+ function hoverLineHeight(lineHeight: number) {
24
+ lineHeightSelected.value = false
25
+
26
+ editor.value.chain().focus().setLineHeight(lineHeight.toString()).run()
27
+ }
28
+
29
+ function resetLineHeight() {
30
+ if (lineHeightSelected.value) {
31
+ return
32
+ }
33
+
34
+ initialLineHeight.value
35
+ ? editor.value.chain().focus().setLineHeight(initialLineHeight.value.toString()).run()
36
+ : editor.value.chain().focus().unsetLineHeight().run()
37
+ }
38
+
39
+ function clearLineHeight() {
40
+ editor.value.chain().focus().unsetLineHeight().run()
41
+ emit('close')
42
+ }
43
+
44
+ function setLineHeight() {
45
+ lineHeightSelected.value = true
46
+
47
+ emit('close')
48
+ }
49
+
50
+ function isLineHeightActive(lineHeight: number): boolean {
51
+ return editor.value.isActive('textStyle', { lineHeight }) || lineHeight === initialLineHeight.value
52
+ }
53
+ </script>
54
+
55
+ <template>
56
+ <VList class="tiptapify-line-height-list">
57
+ <VListItem :disabled="lineHeight === null" density="compact" @click="clearLineHeight">
58
+ <VListItemTitle class="font-italic text-grey-darken-1">
59
+ {{ t('defaultValue') }}
60
+ </VListItemTitle>
61
+ </VListItem>
62
+ <VListItem
63
+ v-for="lineHeight in lineHeights"
64
+ :active="isLineHeightActive(lineHeight)"
65
+ :color="lineHeight === initialLineHeight ? 'primary' : ''"
66
+ density="compact"
67
+ @click="setLineHeight"
68
+ @mouseover="hoverLineHeight(lineHeight)"
69
+ @mouseleave="resetLineHeight"
70
+ >
71
+ <VListItemTitle>
72
+ {{ lineHeight }}
73
+ </VListItemTitle>
74
+ </VListItem>
75
+ </VList>
76
+ </template>
77
+
78
+ <style lang="scss" scoped>
79
+ .tiptapify-line-height-list {
80
+ max-height: 390px;
81
+ }
82
+ </style>
@@ -1,35 +1,38 @@
1
1
  <script setup lang="ts">
2
2
 
3
- import * as mdi from '@mdi/js'
4
3
  import { Editor } from "@tiptap/vue-3";
5
4
 
6
5
  import { useI18n } from 'vue-i18n'
7
- import { computed, inject, onMounted, onUnmounted, Ref, ref } from 'vue'
6
+ import { computed, inject, onMounted, onUnmounted, Ref, ref, watch } from 'vue'
8
7
 
9
- import helpers from '@tiptapify/utils/helpers'
8
+ import Dialog from "@tiptapify/components/UI/Dialog.vue"
10
9
 
11
10
  defineProps({
12
11
  variantBtn: { type: String, default() { return 'elevated' }},
13
- variantField: { type: String, default() { return 'solo' }}
12
+ variantField: { type: String, default() { return 'outlined' }}
14
13
  })
15
14
 
16
- const { ucFirst } = helpers
17
-
18
15
  const editor = inject('tiptapifyEditor') as Ref<Editor>
19
16
  const { t } = useI18n()
20
17
 
21
18
  const generateLinkAttrs = () => ({
22
19
  href: '',
23
- target: '_blank',
20
+ target: targetAttrs.value[0],
24
21
  cssClass: '',
25
22
  rel: ''
26
23
  })
27
24
 
28
- const relAttrs = ['alternate', 'author', 'bookmark', 'external', 'help', 'license', 'next', 'nofollow', 'noreferrer', 'noopener', 'prev', 'search', 'tag']
25
+ const relAttrs = ['alternate', 'author', 'bookmark', 'external', 'help', 'license', 'me', 'next', 'nofollow', 'noopener', 'noreferrer', 'opener', 'prev', 'privacy-policy', 'search', 'tag', 'terms-of-service']
26
+
27
+ const targetAttrs = computed(() => [
28
+ { value: '_blank', title: t('dialog.link.target_blank') },
29
+ { value: '_self', title: t('dialog.link.target_self') }
30
+ ])
29
31
 
30
32
  const attrs = ref(generateLinkAttrs())
33
+ const hrefInvalid = ref(false)
31
34
 
32
- const dialog = ref<boolean>(false)
35
+ const dialog = ref(null)
33
36
 
34
37
  const isDisabled = computed(() => {
35
38
  const { href } = attrs.value
@@ -38,11 +41,10 @@ const isDisabled = computed(() => {
38
41
 
39
42
  function apply() {
40
43
  let { href, target, rel, cssClass } = attrs.value
41
- target = target ? '_blank' : '_self'
42
- rel = rel.join(' ')
44
+ rel = rel?.length ? rel.join(' ') : null
43
45
 
44
46
  if (href) {
45
- editor.value.chain().focus().extendMarkRange('link').setLink({ href, target, rel, class: cssClass }).run()
47
+ editor.value.chain().focus().extendMarkRange('link').setLink({ href, target: target.value, rel, class: cssClass }).run()
46
48
  }
47
49
 
48
50
  close()
@@ -55,18 +57,18 @@ function clear() {
55
57
  }
56
58
 
57
59
  function close() {
58
- dialog.value = false
59
-
60
60
  attrs.value = generateLinkAttrs()
61
+
62
+ dialog.value.close()
61
63
  }
62
64
 
63
65
  const showLink = (event: CustomEvent) => {
64
- attrs.value.href = event.detail.link?.href
65
- attrs.value.target = event.detail.link?.target === '_blank'
66
+ attrs.value.href = event.detail.link?.href ?? ''
67
+ attrs.value.target = targetAttrs.value.find(item => item.value === event.detail.link?.target) ?? targetAttrs[0]
66
68
  attrs.value.rel = event.detail.link?.rel?.split(' ')
67
69
  attrs.value.cssClass = event.detail.link?.class
68
70
 
69
- dialog.value = true;
71
+ dialog.value.open()
70
72
  }
71
73
 
72
74
  onMounted(() => {
@@ -76,67 +78,96 @@ onMounted(() => {
76
78
  onUnmounted(() => {
77
79
  window.removeEventListener('tiptapify-show-link', showLink as EventListener)
78
80
  })
79
- </script>
80
81
 
81
- <template>
82
- <VDialog v-model="dialog" max-width="800" absolute @click:outside="close">
83
- <VCard>
84
- <VToolbar class="px-6" density="compact">
85
- <span class="headline">{{ ucFirst(t('dialog.link.title')) }}</span>
82
+ watch(() => attrs.value.href, () => {
83
+ const azAZ09 = 'a-zA-Z0-9'
86
84
 
87
- <VSpacer />
85
+ const regexHrefProto = 'https?:\\/\\/'
86
+ const regexHrefAuth = `([${azAZ09}]+(:[${azAZ09}]+)?@)?`
87
+ const regexHrefDomain = `[${azAZ09}]+(\\.[${azAZ09}]+(-?[${azAZ09}]+)?)+`
88
+ const regexHrefPath = `(\\/[${azAZ09}\\-]+)*`
89
+ const regexHrefFragment = '(#[^\\s]*)?'
90
+ const regexHrefQueryParam = `(\\?[${azAZ09}\\-_]+((\\[[${azAZ09}]+\\])?=[${azAZ09}\\-_%]+)?)?`
91
+ const regexHrefQueryParamExtra = `(&[${azAZ09}\\-_]+((\\[[${azAZ09}]+\\])?=[${azAZ09}\\-_%]+)?)*`
92
+ const regexHref = `${regexHrefProto}${regexHrefAuth}${regexHrefDomain}${regexHrefPath}${regexHrefFragment}${regexHrefQueryParam}${regexHrefQueryParamExtra}`
88
93
 
89
- <VBtn class="mx-0" icon @click="close">
90
- <VIcon :icon="mdi.mdiClose" />
91
- </VBtn>
92
- </VToolbar>
94
+ const regexMailto = `mailto:\\w+@[${azAZ09}]+(\\.[${azAZ09}]+)*`
95
+ const regexTel = 'tel:\\+?[0-9]+'
96
+
97
+ const regexAll = [regexHref, regexMailto, regexTel].join('|')
98
+
99
+ const regex = new RegExp(`^(${regexAll})$`, 'i')
100
+
101
+ hrefInvalid.value = attrs.value.href !== '' && !regex.test(attrs.value.href)
102
+ })
103
+ </script>
93
104
 
105
+ <template>
106
+ <Dialog ref="dialog" module="link">
107
+ <template #content>
94
108
  <VCardText>
95
109
  <VRow>
96
- <VCol cols="12" md="9">
97
- <VTextField v-model="attrs.href" :variant="variantField" :label="ucFirst(t('dialog.link.href'))" autofocus />
110
+ <VCol cols="12">
111
+ <VTextField
112
+ v-model="attrs.href"
113
+ density="compact"
114
+ variant="outlined"
115
+ :label="t('dialog.link.href')"
116
+ :error-messages="hrefInvalid ? t('dialog.link.href_error') : ''"
117
+ autofocus
118
+ />
119
+ </VCol>
120
+
121
+ <VCol cols="12" md="4">
122
+ <VSelect
123
+ v-model="attrs.target"
124
+ :items="targetAttrs"
125
+ :label="t('dialog.link.target')"
126
+ variant="outlined"
127
+ return-object
128
+ density="compact"
129
+ />
98
130
  </VCol>
99
131
 
100
- <VCol cols="12" md="3">
101
- <VCheckbox v-model="attrs.target" color="primary" :label="ucFirst(t('dialog.link.target'))" />
132
+ <VCol cols="12" md="8">
133
+ <VTextField v-model="attrs.cssClass" density="compact" variant="outlined" :label="t('dialog.link.class')" />
102
134
  </VCol>
103
135
 
104
136
  <VCol cols="12">
105
137
  <VSelect
106
138
  v-model="attrs.rel"
107
139
  :items="relAttrs"
108
- :label="ucFirst(t('dialog.link.rel'))"
109
- :variant="variantField"
140
+ :label="t('dialog.link.rel')"
141
+ variant="outlined"
110
142
  multiple
111
143
  chips
112
144
  closable-chips
113
145
  clearable
146
+ density="compact"
114
147
  />
115
148
  </VCol>
116
-
117
- <VCol cols="12">
118
- <VTextField v-model="attrs.cssClass" :variant="variantField" :label="ucFirst(t('dialog.link.class'))" />
119
- </VCol>
120
149
  </VRow>
121
150
  </VCardText>
151
+ </template>
122
152
 
153
+ <template #actions>
123
154
  <VCardActions>
124
155
  <VRow>
125
156
  <VCol class="d-flex justify-start">
126
157
  <VBtn color="warning" v-if="editor.isActive('link')" :variant="variantBtn" :disabled="isDisabled" @click="clear">
127
- {{ ucFirst(t('dialog.clear')) }}
158
+ {{ t('dialog.clear') }}
128
159
  </VBtn>
129
160
  </VCol>
130
161
  <VCol class="d-flex justify-end">
131
162
  <VBtn :variant="variantBtn" @click="close" class="mr-2">
132
- {{ ucFirst(t('dialog.close')) }}
163
+ {{ t('dialog.close') }}
133
164
  </VBtn>
134
- <VBtn color="primary" :variant="variantBtn" :disabled="isDisabled" @click="apply">
135
- {{ ucFirst(t('dialog.apply')) }}
165
+ <VBtn color="primary" :variant="variantBtn" :disabled="isDisabled || hrefInvalid" @click="apply">
166
+ {{ t('dialog.apply') }}
136
167
  </VBtn>
137
168
  </VCol>
138
169
  </VRow>
139
170
  </VCardActions>
140
- </VCard>
141
- </VDialog>
171
+ </template>
172
+ </Dialog>
142
173
  </template>
@@ -1,17 +1,15 @@
1
1
  <script setup lang="ts">
2
+ import Dialog from "@tiptapify/components/UI/Dialog.vue";
2
3
  import { ref, onMounted, onUnmounted } from 'vue'
3
- import { useI18n } from "vue-i18n";
4
- import * as mdi from '@mdi/js'
5
-
6
- const { t } = useI18n();
7
4
 
8
5
  const content = ref()
9
6
 
10
- const dialog = ref(false)
7
+ const dialog = ref(null)
11
8
 
12
9
  const showDialog = (event: CustomEvent) => {
13
10
  content.value = event.detail.html
14
- dialog.value = true;
11
+
12
+ dialog.value.open()
15
13
  }
16
14
 
17
15
  onMounted(() => {
@@ -24,19 +22,13 @@ onUnmounted(() => {
24
22
  </script>
25
23
 
26
24
  <template>
27
- <VDialog v-model="dialog" fullscreen>
28
- <VCard>
29
- <VToolbar>
30
- <VBtn :icon="mdi.mdiClose" @click="dialog = false" />
31
-
32
- <VToolbarTitle>Preview</VToolbarTitle>
33
- </VToolbar>
34
-
25
+ <Dialog ref="dialog" module="preview" fullscreen>
26
+ <template #content>
35
27
  <VCardItem>
36
28
  <div class="tiptap" v-html="content"></div>
37
29
  </VCardItem>
38
- </VCard>
39
- </VDialog>
30
+ </template>
31
+ </Dialog>
40
32
  </template>
41
33
 
42
34
  <style lang="scss">
@@ -1,30 +1,27 @@
1
1
  <script setup lang="ts">
2
2
  import { Editor } from "@tiptap/vue-3";
3
+ import Dialog from "@tiptapify/components/UI/Dialog.vue";
3
4
  import { ref, onMounted, onUnmounted, watch, inject, Ref } from 'vue'
4
5
  import { useI18n } from "vue-i18n";
5
6
 
6
- import helpers from "@tiptapify/utils/helpers";
7
-
8
7
  const props = defineProps({
9
8
  indent: { type: Number, default: 2 },
10
9
  variantBtn: { type: String, default: 'elevated' },
11
10
  variantField: { type: String, default: 'solo' }
12
11
  })
13
12
 
14
- const { ucFirst } = helpers;
15
-
16
13
  const { t } = useI18n();
17
14
 
18
15
  const editor = inject('tiptapifyEditor') as Ref<Editor>
19
16
 
20
- const dialog = ref(false)
17
+ const dialog = ref(null)
21
18
  const formatted = ref(false)
22
19
  const sourceCode = ref('')
23
20
 
24
21
  const formatHtml = (html: string): string => {
22
+ const singleTags = ['img', 'hr', 'br', 'input']
25
23
  let formatted = html.replace(/>/g, '>\n');
26
24
 
27
- formatted = formatted.replace(/\n</g, '\n<');
28
25
  formatted = formatted.replace(/([^>\n])</g, '$1\n<');
29
26
 
30
27
  const lines = formatted.split('\n');
@@ -38,7 +35,8 @@ const formatHtml = (html: string): string => {
38
35
 
39
36
  const indentedLine = ' '.repeat(indentLevel * props.indent) + line;
40
37
 
41
- if (line.match(/<[^\/][^>]*>/) && !line.match(/<.*\/>/)) {
38
+ const tag = line.match(/<\/?(\S+).*>/) ?? []
39
+ if (!singleTags.includes(tag[1] ?? '') && line.match(/<[^\/][^>]*>/) && !line.match(/<.*\/>/)) {
42
40
  indentLevel++;
43
41
  }
44
42
 
@@ -57,11 +55,12 @@ const unformatHtml = (html: string): string => {
57
55
 
58
56
  const showDialog = (event: CustomEvent) => {
59
57
  sourceCode.value = event.detail.html
60
- dialog.value = true;
58
+
59
+ dialog.value.open()
61
60
  }
62
61
 
63
62
  const saveChanges = () => {
64
- dialog.value = false
63
+ dialog.value.close()
65
64
 
66
65
  editor.value.commands.setContent(sourceCode.value, true)
67
66
  }
@@ -80,16 +79,14 @@ watch(() => formatted.value, () => {
80
79
  </script>
81
80
 
82
81
  <template>
83
- <VDialog v-model="dialog" max-width="1500">
84
- <VCard>
85
- <VCardTitle>{{ ucFirst(t('dialog.source.title')) }}</VCardTitle>
86
-
82
+ <Dialog ref="dialog" module="source" :max-width="1500">
83
+ <template #content>
87
84
  <VCardText>
88
85
  <VContainer fluid class="pt-0 pl-0 pr-0">
89
86
  <VRow>
90
87
  <VCol>
91
88
  <VBtn v-model="formatted" :color="`${formatted ? 'primary' : ''}`" @click="formatted = !formatted">
92
- {{ ucFirst(t('dialog.source.prettify')) }}
89
+ {{ t('dialog.source.prettify') }}
93
90
  </VBtn>
94
91
  </VCol>
95
92
  </VRow>
@@ -103,18 +100,20 @@ watch(() => formatted.value, () => {
103
100
  class="source-code-area"
104
101
  />
105
102
  </VCardText>
103
+ </template>
106
104
 
105
+ <template #actions>
107
106
  <VCardActions>
108
107
  <VSpacer></VSpacer>
109
108
  <VBtn :variant="variantBtn" @click="dialog = false">
110
- {{ ucFirst(t('dialog.close')) }}
109
+ {{ t('dialog.close') }}
111
110
  </VBtn>
112
111
  <VBtn :variant="variantBtn" color="primary" @click="saveChanges">
113
- {{ ucFirst(t('dialog.apply')) }}
112
+ {{ t('dialog.apply') }}
114
113
  </VBtn>
115
114
  </VCardActions>
116
- </VCard>
117
- </VDialog>
115
+ </template>
116
+ </Dialog>
118
117
  </template>
119
118
 
120
119
  <style scoped lang="scss">