tiptapify 0.2.2 → 0.2.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tiptapify",
3
3
  "types": "./index.d.ts",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "description": "Tiptap3 editor with Vuetify3 menu implementation",
6
6
  "exports": {
7
7
  ".": {
@@ -1,11 +1,17 @@
1
1
  <script setup lang="ts">
2
2
  import { Editor } from '@tiptap/vue-3'
3
- import { computed, inject, ref, Ref } from 'vue'
3
+ import { TiptapifyFooterAlignment } from '@tiptapify/types/editor'
4
+ import { computed, inject, PropType, ref, Ref } from 'vue'
4
5
  import { ComposerTranslation } from 'vue-i18n'
5
6
 
6
7
  const props = defineProps({
7
8
  showWordsCount: { type: Boolean, default: true },
8
9
  showCharactersCount: { type: Boolean, default: true },
10
+ limit: { type: Number, default: 0 },
11
+ limitDefaultColor: { type: String, default: 'purple' },
12
+ limitAlertColor: { type: String, default: 'orange' },
13
+ limitWarningColor: { type: String, default: 'red' },
14
+ alignment: { type: String as PropType<TiptapifyFooterAlignment>, default: 'end' },
9
15
  })
10
16
 
11
17
  const { t } = inject('tiptapifyI18n') as { t: ComposerTranslation }
@@ -25,6 +31,17 @@ const statusItems = ref([
25
31
  }
26
32
  ])
27
33
 
34
+ const percentage = computed(() => {
35
+ if (props.limit === 0) {
36
+ return 100
37
+ }
38
+ return Math.round((100 / props.limit) * editor.value.storage.characterCount.characters())
39
+ })
40
+ const limitText = computed(() => {
41
+ const current = editor.value.storage.characterCount.characters()
42
+ return `${t('footer.limit')}<br>${current} / ${props.limit}`
43
+ })
44
+
28
45
  function printStatusItemText(text: string): string {
29
46
  return t(text)
30
47
  }
@@ -37,7 +54,42 @@ function showFooter() {
37
54
  <template>
38
55
  <div v-if="showFooter()" class="tiptapify-footer">
39
56
  <VRow>
40
- <VCol class="d-flex justify-end">
57
+ <VCol class="d-flex align-center" :class="`justify-${alignment}`">
58
+ <template v-if="limit > 0">
59
+ <VTooltip>
60
+ <template #default>
61
+ <span v-html="limitText" />
62
+ </template>
63
+ <template #activator="{ props }">
64
+ <svg
65
+ height="25"
66
+ width="25"
67
+ viewBox="0 0 20 20"
68
+ v-bind="props"
69
+ :class="{
70
+ 'character-count': true,
71
+ 'character-count--alert': percentage > 75,
72
+ 'character-count--warning': editor.storage.characterCount.characters() === limit,
73
+ }"
74
+ >
75
+ <circle r="8" cx="10" cy="10" fill="#e9ecef" />
76
+ <circle
77
+ r="5"
78
+ cx="10"
79
+ cy="10"
80
+ fill="transparent"
81
+ stroke="currentColor"
82
+ stroke-width="6"
83
+ :stroke-dasharray="`calc(${percentage} * 31.4 / 100) 31.4`"
84
+ transform="rotate(-90) translate(-20)"
85
+ />
86
+ <circle r="6" cx="10" cy="10" fill="white" />
87
+ </svg>
88
+ </template>
89
+ </VTooltip>
90
+
91
+ <VDivider class="tiptapify-footer--divider" vertical />
92
+ </template>
41
93
  <template v-for="statusItem in statusItems" :key="statusItem.text">
42
94
  <span v-if="statusItem.enabled" class="tiptapify-footer--status-item">
43
95
  {{ printStatusItemText(statusItem.text) }}: {{ statusItem.value }}
@@ -67,4 +119,17 @@ function showFooter() {
67
119
  .tiptapify-footer--divider:last-child {
68
120
  display: none;
69
121
  }
122
+
123
+ /* Character count */
124
+ .character-count {
125
+ color: v-bind(limitDefaultColor);
126
+
127
+ &--alert {
128
+ color: v-bind(limitAlertColor);
129
+ }
130
+
131
+ &--warning {
132
+ color: v-bind(limitWarningColor);
133
+ }
134
+ }
70
135
  </style>
@@ -6,7 +6,7 @@ import { SlashCommandsConfig } from '@tiptapify/types/slashCommandsTypes'
6
6
  import { computed, onBeforeUnmount, PropType, provide, ref, ShallowRef } from 'vue'
7
7
  import { default as Toolbar } from '@tiptapify/components/Toolbar/Index.vue'
8
8
  import { Editor, EditorContent } from '@tiptap/vue-3'
9
- import { TiptapifyAiOptions, TiptapifyEditor, variantBtnTypes, variantFieldTypes } from '@tiptapify/types/editor'
9
+ import { TiptapifyAiOptions, TiptapifyEditor, TiptapifyFooterAlignment, variantBtnTypes, variantFieldTypes } from '@tiptapify/types/editor'
10
10
  import MenuBubble from '@tiptapify/components/MenuBubble.vue'
11
11
  import MenuFloating from '@tiptapify/components/MenuFloating.vue'
12
12
 
@@ -33,6 +33,10 @@ const props = defineProps({
33
33
  showWordsCount: { type: Boolean, default () { return true } },
34
34
  showCharactersCount: { type: Boolean, default () { return true } },
35
35
  limit: { type: Number as PropType<number | null>, default () { return null } },
36
+ limitDefaultColor: { type: String, default () { return 'purple' } },
37
+ limitAlertColor: { type: String, default () { return 'orange' } },
38
+ limitWarningColor: { type: String, default () { return 'red' } },
39
+ footerAlignment: { type: String as PropType<TiptapifyFooterAlignment>, default () { return 'end' } },
36
40
  defaultFontFamily: { type: String, default () { return 'Inter' } },
37
41
  fontMeasure: { type: String, default () { return 'px' } },
38
42
  rounded: { type: String, default () { return '0' } },
@@ -154,7 +158,15 @@ onBeforeUnmount(() => {
154
158
  <EditorContent :editor="editor" class="tiptapify-editor" />
155
159
  </div>
156
160
 
157
- <Footer :show-words-count="showWordsCount" :show-characters-count="showCharactersCount" />
161
+ <Footer
162
+ :show-words-count="showWordsCount"
163
+ :show-characters-count="showCharactersCount"
164
+ :limit="limit ?? 0"
165
+ :limit-default-color="limitDefaultColor"
166
+ :limit-alert-color="limitAlertColor"
167
+ :limit-warning-color="limitWarningColor"
168
+ :alignment="footerAlignment"
169
+ />
158
170
  </div>
159
171
  </div>
160
172
  </template>
@@ -39,7 +39,6 @@ import { toolbarSections } from '@tiptapify/types/toolbarTypes'
39
39
 
40
40
  // load all languages with "all" or common languages with "common"
41
41
  import { common, createLowlight } from 'lowlight'
42
-
43
42
  // create a lowlight instance
44
43
  // using all available languages
45
44
  const lowlight = createLowlight(common)
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "كلمات",
174
- "chars": "حروف"
174
+ "chars": "حروف",
175
+ "limit": "حد الأحرف"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "单词",
174
- "chars": "字符"
174
+ "chars": "字符",
175
+ "limit": "字数限制"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "slova",
174
- "chars": "znaky"
174
+ "chars": "znaky",
175
+ "limit": "limit znaků"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "Wörter",
174
- "chars": "Zeichen"
174
+ "chars": "Zeichen",
175
+ "limit": "Zeichenlimit"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -170,7 +170,8 @@
170
170
  },
171
171
  "footer": {
172
172
  "words": "words",
173
- "chars": "characters"
173
+ "chars": "characters",
174
+ "limit": "characters limit"
174
175
  },
175
176
  "ai": {
176
177
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "palabras",
174
- "chars": "caracteres"
174
+ "chars": "caracteres",
175
+ "limit": "límite de caracteres"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "sanaa",
174
- "chars": "merkkiä"
174
+ "chars": "merkkiä",
175
+ "limit": "merkkien enimmäismäärä"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "mots",
174
- "chars": "caractères"
174
+ "chars": "caractères",
175
+ "limit": "limite de caractères"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "szavak",
174
- "chars": "karakterek"
174
+ "chars": "karakterek",
175
+ "limit": "karakterkorlát"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "parole",
174
- "chars": "caratteri"
174
+ "chars": "caratteri",
175
+ "limit": "limite caratteri"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "単語数",
174
- "chars": "文字数"
174
+ "chars": "文字数",
175
+ "limit": "文字制限"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "단어",
174
- "chars": "글자"
174
+ "chars": "글자",
175
+ "limit": "글자 제한"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "vārdi",
174
- "chars": "zīmes"
174
+ "chars": "zīmes",
175
+ "limit": "zīmju ierobežojums"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "žodžiai",
174
- "chars": "simboliai"
174
+ "chars": "simboliai",
175
+ "limit": "simbolių limitas"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "woorden",
174
- "chars": "tekens"
174
+ "chars": "tekens",
175
+ "limit": "tekens limiet"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "słowa",
174
- "chars": "znaki"
174
+ "chars": "znaki",
175
+ "limit": "limit znaków"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "palavras",
174
- "chars": "caracteres"
174
+ "chars": "caracteres",
175
+ "limit": "limite de caracteres"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "слова",
174
- "chars": "символы"
174
+ "chars": "символы",
175
+ "limit": "лимит символов"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "ord",
174
- "chars": "tecken"
174
+ "chars": "tecken",
175
+ "limit": "teckengräns"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "คำ",
174
- "chars": "ตัวอักษร"
174
+ "chars": "ตัวอักษร",
175
+ "limit": "ขีดจำกัดตัวอักษร"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "kelime",
174
- "chars": "karakter"
174
+ "chars": "karakter",
175
+ "limit": "karakter sınırı"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "слова",
174
- "chars": "символи"
174
+ "chars": "символи",
175
+ "limit": "ліміт символів"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -171,7 +171,8 @@
171
171
  },
172
172
  "footer": {
173
173
  "words": "từ ngữ",
174
- "chars": "ký tự"
174
+ "chars": "ký tự",
175
+ "limit": "giới hạn ký tự"
175
176
  },
176
177
  "ai": {
177
178
  "title": "AI assistant",
@@ -9,6 +9,8 @@ export { TiptapifyEditor }
9
9
  export type variantBtnTypes = 'outlined' | 'plain' | 'flat' | 'text' | 'elevated' | 'tonal' | undefined
10
10
  export type variantFieldTypes = 'outlined' | 'plain' | 'filled' | 'solo' | 'solo-filled' | 'solo-inverted' | 'underlined' | undefined
11
11
 
12
+ export type TiptapifyFooterAlignment = 'start' | 'center' | 'end'
13
+
12
14
  export type TiptapifyAiMode = 'insert' | 'replace' | 'append'
13
15
 
14
16
  export type TiptapifyAiPromptExample = {