tiptapify 0.0.1 → 0.0.4

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,6 +1,6 @@
1
1
  {
2
2
  "name": "tiptapify",
3
- "version": "0.0.1",
3
+ "version": "0.0.4",
4
4
  "description": "Tiptap3 editor with Vuetify3 menu implementation",
5
5
  "exports": {
6
6
  ".": {
@@ -17,11 +17,25 @@
17
17
  "test": "echo \"Error: no test specified\" && exit 1"
18
18
  },
19
19
  "keywords": [
20
- "vue", "vue3", "vue.js", "typescript", "vuetify", "vuetify3", "tiptap", "tiptap3", "editor", "material design",
21
- "wysiwyg", "markdown"
20
+ "vue",
21
+ "vue3",
22
+ "vue.js",
23
+ "typescript",
24
+ "vuetify",
25
+ "vuetify3",
26
+ "tiptap",
27
+ "tiptap3",
28
+ "tiptap menu",
29
+ "tiptap slash command",
30
+ "tiptap toolbar",
31
+ "editor",
32
+ "material design",
33
+ "wysiwyg",
34
+ "markdown"
22
35
  ],
23
36
  "author": "Igor Voytovich",
24
37
  "license": "MIT",
38
+ "repository": "https://github.com/IVoyt/tiptapify",
25
39
  "packageManager": "pnpm@10.11.0",
26
40
  "dependencies": {
27
41
  "@intlify/unplugin-vue-i18n": "^6.0.8",
@@ -68,12 +82,11 @@
68
82
  "highlight.js": "^11.11.1",
69
83
  "linkifyjs": "^4.3.1",
70
84
  "lowlight": "^3.3.0",
71
- "tippy.js": "^6.3.7",
72
85
  "unplugin-vue-components": "^28.5.0",
73
86
  "vite": "^6.3.5",
74
87
  "vite-plugin-vuetify": "^2.1.1",
75
88
  "vite-svg-loader": "^5.1.0",
76
- "vue-i18n": "^11.1.3"
89
+ "vue-i18n": "^11.1.4"
77
90
  },
78
91
  "peerDependencies": {
79
92
  "@mdi/js": "^7.4.47",
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import LinkDialog from "@tiptapify/components/extensions/components/LinkDialog.vue";
3
3
  import { useEditor } from "@tiptapify/composable/useEditor";
4
- import { defineProps, ref } from "vue";
4
+ import { computed, defineProps, ref } from "vue";
5
5
  import { BubbleMenu } from '@tiptap/vue-3/menus'
6
6
  import * as mdi from '@mdi/js'
7
7
 
@@ -14,6 +14,77 @@ const editorInstance = ref(editor.getInstance())
14
14
 
15
15
  const bubbleMenuLinkButton = ref(null)
16
16
 
17
+ const items = ref([
18
+ {
19
+ name: 'bold',
20
+ icon: mdi.mdiFormatBold,
21
+ props: {
22
+ active: false,
23
+ disabled: computed(() => !editorInstance.value.can().chain().focus().toggleBold().run()),
24
+ color: computed(() => editorInstance.value.isActive('bold') ? 'primary' : ''),
25
+ },
26
+ click: () => editorInstance.value.chain().focus().toggleBold().run(),
27
+ },
28
+ {
29
+ name: 'italic',
30
+ icon: mdi.mdiFormatItalic,
31
+ props: {
32
+ active: false,
33
+ disabled: computed(() => !editorInstance.value.can().chain().focus().toggleItalic().run()),
34
+ color: computed(() => editorInstance.value.isActive('italic') ? 'primary' : ''),
35
+ },
36
+ click: () => editorInstance.value.chain().focus().toggleItalic().run(),
37
+ },
38
+ {
39
+ name: 'strike',
40
+ icon: mdi.mdiFormatStrikethroughVariant,
41
+ props: {
42
+ active: false,
43
+ disabled: computed(() => !editorInstance.value.can().chain().focus().toggleStrike().run()),
44
+ color: computed(() => editorInstance.value.isActive('strike') ? 'primary' : ''),
45
+ },
46
+ click: () => editorInstance.value.chain().focus().toggleStrike().run(),
47
+ },
48
+ {
49
+ name: 'underline',
50
+ icon: mdi.mdiFormatUnderline,
51
+ props: {
52
+ active: false,
53
+ disabled: computed(() => !editorInstance.value.can().chain().focus().toggleUnderline().run()),
54
+ color: computed(() => editorInstance.value.isActive('underline') ? 'primary' : ''),
55
+ },
56
+ click: () => editorInstance.value.chain().focus().toggleUnderline().run(),
57
+ },
58
+ {
59
+ name: 'highlight',
60
+ icon: mdi.mdiFormatColorHighlight,
61
+ props: {
62
+ active: false,
63
+ disabled: computed(() => !editorInstance.value.can().chain().focus().toggleHighlight().run()),
64
+ color: computed(() => editorInstance.value.isActive('highlight') ? 'primary' : ''),
65
+ },
66
+ click: () => editorInstance.value.chain().focus().toggleHighlight().run(),
67
+ },
68
+ {
69
+ name: 'link',
70
+ icon: computed(() => editorInstance.value.isActive('link') ? mdi.mdiLinkOff : mdi.mdiLink),
71
+ props: {
72
+ active: false,
73
+ color: computed(() => editorInstance.value.isActive('link') ? 'primary' : ''),
74
+ disabled: computed(() => editorInstance.value.isActive('code') || editorInstance.value.isActive('codeBlock')),
75
+ },
76
+ click: () => linkAction(),
77
+ },
78
+ {
79
+ name: 'format clear',
80
+ icon: mdi.mdiFormatClear,
81
+ props: {
82
+ active: false,
83
+ },
84
+ click: () => editorInstance.value.chain().focus().unsetAllMarks().clearNodes().run(),
85
+ }
86
+ ])
87
+
17
88
  function linkAction() {
18
89
  return editorInstance.value.isActive('link')
19
90
  ? editorInstance.value.chain().focus().unsetLink().run()
@@ -23,32 +94,11 @@ function linkAction() {
23
94
 
24
95
  <template>
25
96
  <!-- <BubbleMenu v-if="editorInstance" :editor="editorInstance" :tippy-options="{ duration: 100 }">-->
26
- <BubbleMenu v-if="editorInstance" :editor="editorInstance">
97
+ <BubbleMenu v-if="editorInstance" :editor="editorInstance" :options="{ placement: 'bottom' }">
27
98
  <div class="bubble-menu">
28
99
  <VBtnToggle divided density="compact" :variant="variant">
29
- <VBtn
30
- @click="editorInstance.chain().focus().toggleBold().run()"
31
- :active="editorInstance.isActive('bold')"
32
- size="small"
33
- >
34
- <VIcon :icon="mdi.mdiFormatBold" size="16" />
35
- </VBtn>
36
- <VBtn
37
- @click="editorInstance.chain().focus().toggleItalic().run()"
38
- :active="editorInstance.isActive('italic')"
39
- size="small"
40
- >
41
- <VIcon :icon="mdi.mdiFormatItalic" size="16" />
42
- </VBtn>
43
- <VBtn
44
- @click="editorInstance.chain().focus().toggleStrike().run()"
45
- :active="editorInstance.isActive('strike')"
46
- size="small"
47
- >
48
- <VIcon :icon="mdi.mdiFormatStrikethroughVariant" size="16" />
49
- </VBtn>
50
- <VBtn @click="linkAction" :active="editorInstance.isActive('link')" size="small">
51
- <VIcon :icon="`${ editorInstance.isActive('link') ? mdi.mdiLinkOff : mdi.mdiLink}`" size="16" />
100
+ <VBtn v-for="(item, key) in items" :key="key" v-bind="item.props" @click="item.click" size="small">
101
+ <VIcon :icon="item.icon" size="16" />
52
102
  </VBtn>
53
103
  </VBtnToggle>
54
104
  </div>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
 
3
3
  import { onBeforeUnmount, ref } from "vue";
4
- import Toolbar from "@tiptapify/components/Toolbar.vue";
4
+ import { default as Toolbar } from "@tiptapify/components/Toolbar/Index.vue";
5
5
  import { EditorContent } from '@tiptap/vue-3'
6
6
  import { useEditor } from '@tiptapify/composable/useEditor'
7
7
  import MenuBubble from '@tiptapify/components/MenuBubble.vue'
@@ -11,16 +11,20 @@ import Footer from '@tiptapify/components/Footer.vue'
11
11
 
12
12
  const props = defineProps({
13
13
  content: String|Object,
14
- variant: { type: String, default () { return 'outline' } },
15
- menu: { type: Boolean, default () { return true } },
14
+ variant: { type: String, default () { return 'flat' } },
15
+ toolbar: { type: Boolean, default () { return true } },
16
+ items: { type: Array<string>, default() { return [] }},
17
+ itemsExclude: { type: Boolean, default() { return false } },
16
18
  bubbleMenu: { type: Boolean, default () { return true } },
17
19
  floatingMenu: { type: Boolean, default () { return true } },
20
+ slashCommands: { type: Boolean, default () { return true } },
18
21
  placeholder: { type: String, default () { return 'Write something here...' } },
19
22
  showCharacterCount: { type: Boolean, default () { return true } },
20
23
  defaultFontFamily: { type: String, default () { return 'Inter' } },
24
+ fontMeasure: { type: String, default () { return 'px' } },
21
25
  })
22
26
 
23
- const editor = useEditor(props.content, props.placeholder).editor
27
+ const editor = useEditor(props.content, props.placeholder, props.slashCommands).editor
24
28
  const editorInstance = ref(editor.getInstance())
25
29
  editorInstance?.value?.chain().setFontFamily(props.defaultFontFamily).run()
26
30
 
@@ -35,8 +39,14 @@ onBeforeUnmount(() => {
35
39
  <VRow>
36
40
  <VCol>
37
41
  <div class="border rounded">
38
- <template v-if="menu">
39
- <Toolbar v-if="editorInstance" :variant="variant" />
42
+ <template v-if="toolbar">
43
+ <Toolbar
44
+ v-if="editorInstance"
45
+ :variant="variant"
46
+ :font-measure="fontMeasure"
47
+ :items="items"
48
+ :items-exclude="itemsExclude"
49
+ />
40
50
  </template>
41
51
 
42
52
  <div class="pa-2 tiptapify-container">
@@ -0,0 +1,142 @@
1
+ <script setup lang="ts">
2
+ import LinkDialog from "@tiptapify/components/extensions/components/LinkDialog.vue";
3
+ import { useEditor } from "@tiptapify/composable/useEditor";
4
+ import { computed, defineProps, Ref, ref } from 'vue'
5
+ import { useI18n } from "vue-i18n";
6
+
7
+ import { toolbarItems, ToolbarItemSections } from "@tiptapify/components/Toolbar/items";
8
+
9
+ const props = defineProps({
10
+ variant: { type: String, default () { return 'flat' }},
11
+ items: { type: Array<string>, default() { return [] }},
12
+ itemsExclude: { type: Boolean, default() { return false } },
13
+ headingLevels: { type: Array<number>, default() { return [] }},
14
+ fontMeasure: { type: String, default () { return 'px' }},
15
+ customFonts: { type: Array<string>, default () { return [] } },
16
+ customFontsOverride: { type: Boolean, default() { return false } },
17
+ })
18
+
19
+ const { t } = useI18n();
20
+
21
+ const { editor } = useEditor()
22
+ const editorInstance = ref(editor.getInstance())
23
+
24
+ const toolbarLinkButton = ref(null)
25
+
26
+ const items = toolbarItems(
27
+ editorInstance,
28
+ computed(() => props.fontMeasure).value,
29
+ { list: computed(() => props.items).value, exclude: computed(() => props.itemsExclude).value },
30
+ computed(() => props.headingLevels).value,
31
+ toolbarLinkButton
32
+ )
33
+ const toolbarItemsRef: Ref<ToolbarItemSections> = ref(items)
34
+
35
+ </script>
36
+
37
+ <template>
38
+ <div v-if="editor" class="d-flex flex-wrap gap-x-4 gap-y-2 tiptapify-menu">
39
+ <template v-for="(toolbarItems, sectionKey) in toolbarItemsRef" :key="sectionKey">
40
+ <template v-for="(toolbarItem, toolbarItemKey) in toolbarItems" :key="toolbarItemKey">
41
+ <VDivider v-if="toolbarItem.name === '|'" vertical class="menu-divider" />
42
+
43
+ <template v-else-if="toolbarItem.enabled">
44
+ <template v-if="toolbarItem.children">
45
+ <VBtnToggle v-if="toolbarItem.group" :variant="variant">
46
+ <VBtn
47
+ v-for="(item, key) of toolbarItem.children"
48
+ v-bind="{ ...props, ...item.props}" v-on="item.attrs" size="32"
49
+ :key="`${item.name}-${key}`"
50
+ >
51
+ <VTooltip :text="t(item.name)" location="top" activator="parent" />
52
+
53
+ <VIcon v-if="item.icon" :icon="item.icon" size="small" />
54
+ <span v-else class="menu-item-title">
55
+ {{ t(toolbarItem.name) }}
56
+ </span>
57
+ </VBtn>
58
+ </VBtnToggle>
59
+
60
+ <VMenu v-else>
61
+ <template #activator="{ props: menuProps }">
62
+ <VBtn :variant="variant" v-bind="menuProps" size="32" class="menu-button">
63
+ <VTooltip :text="t(toolbarItem.tooltip)" location="top" activator="parent" />
64
+
65
+ <VIcon v-if="toolbarItem.icon" :icon="toolbarItem.icon" size="small" />
66
+ <span v-else class="menu-item-title">
67
+ {{ t(toolbarItem.name) }}
68
+ </span>
69
+ </VBtn>
70
+ </template>
71
+
72
+ <VList v-model="toolbarItem.modelValue" max-height="430px">
73
+ <VListItem
74
+ v-for="(item, menuItemKey) in toolbarItem.children"
75
+ :key="menuItemKey"
76
+ :value="item.name"
77
+ density="compact"
78
+ v-bind="item.props"
79
+ v-on="item.attrs"
80
+ >
81
+ <VTooltip v-if="item.tooltip" :text="t(item.tooltip)" location="top" activator="parent" />
82
+
83
+ <VListItemTitle>
84
+ <VIcon v-if="item.icon" :icon="item.icon" size="small" />
85
+ <span v-else class="menu-item-title">
86
+ <template v-if="item.noI18n">
87
+ {{ item.name }}
88
+ </template>
89
+ <template v-else>
90
+ {{ t(item.name) }}
91
+ </template>
92
+ </span>
93
+ </VListItemTitle>
94
+ </VListItem>
95
+ </VList>
96
+ </VMenu>
97
+ </template>
98
+
99
+ <VBtn v-else :variant="variant" v-bind="toolbarItem.props" v-on="toolbarItem.attrs" class="menu-button" size="32">
100
+ <VTooltip :text="t(toolbarItem.tooltip)" location="top" activator="parent" />
101
+
102
+ <VIcon v-if="toolbarItem.icon" :icon="toolbarItem.icon" size="16" />
103
+ <span v-else class="menu-item-title">
104
+ {{ t(toolbarItem.name) }}
105
+ </span>
106
+ </VBtn>
107
+ </template>
108
+ </template>
109
+
110
+ <VDivider vertical class="menu-divider" />
111
+ </template>
112
+
113
+ <LinkDialog ref="toolbarLinkButton" />
114
+ </div>
115
+ </template>
116
+
117
+ <style lang="scss" scoped>
118
+ .tiptapify-menu {
119
+ padding: 8px;
120
+ border-bottom: var(--border);
121
+ }
122
+
123
+ :deep(.v-btn-group) {
124
+ height: 32px !important;
125
+ }
126
+
127
+ .menu-item-title {
128
+ font-size: 14px;
129
+ }
130
+
131
+ .menu-button {
132
+ margin: 0 1px;
133
+ }
134
+
135
+ .v-divider.menu-divider {
136
+ margin: 0 10px;
137
+ }
138
+
139
+ .v-divider.menu-divider:nth-last-child(1) {
140
+ display: none;
141
+ }
142
+ </style>