tiptapify 0.0.35 → 0.1.0
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/README.md +12 -6
- package/package.json +7 -3
- package/src/components/Tiptapify.vue +95 -2
- package/src/components/Toolbar/Index.vue +10 -0
- package/src/components/Toolbar/Items.vue +17 -13
- package/src/components/Toolbar/media.ts +5 -0
- package/src/components/editorExtensions.ts +15 -6
- package/src/components/index.ts +2 -1
- package/src/extensions/PickerEventBus.ts +32 -0
- package/src/extensions/charmap/arrows.ts +1227 -0
- package/src/extensions/charmap/box_drawing.ts +324 -0
- package/src/extensions/charmap/currency.ts +157 -0
- package/src/extensions/charmap/cyrillic.ts +646 -0
- package/src/extensions/charmap/diacritics.ts +107 -0
- package/src/extensions/charmap/extended_letters.ts +1311 -0
- package/src/extensions/charmap/greek.ts +443 -0
- package/src/extensions/charmap/hebrew.ts +177 -0
- package/src/extensions/charmap/index.ts +75 -0
- package/src/extensions/charmap/math.ts +2949 -0
- package/src/extensions/charmap/punctuation.ts +121 -0
- package/src/extensions/charmap/symbols.ts +506 -0
- package/src/extensions/charmap/typography.ts +499 -0
- package/src/extensions/components/media/charmap/Button.vue +27 -0
- package/src/extensions/components/media/charmap/Picker.vue +229 -0
- package/src/extensions/components/media/emoji/Button.vue +6 -147
- package/src/extensions/components/media/emoji/Picker.vue +225 -0
- package/src/extensions/components/media/image/ImageDialog.vue +69 -27
- package/src/extensions/components/slashCommands/CommandsList.vue +65 -22
- package/src/extensions/components/slashCommands/PickerDialog.vue +44 -0
- package/src/extensions/components/slashCommands/suggestion.ts +152 -105
- package/src/extensions/slash-commands.ts +169 -9
- package/src/i18n/locales/ar.json +37 -14
- package/src/i18n/locales/ch.json +37 -14
- package/src/i18n/locales/cz.json +37 -14
- package/src/i18n/locales/de.json +37 -14
- package/src/i18n/locales/en.json +34 -12
- package/src/i18n/locales/es.json +37 -14
- package/src/i18n/locales/fi.json +37 -14
- package/src/i18n/locales/fr.json +37 -14
- package/src/i18n/locales/hu.json +37 -14
- package/src/i18n/locales/it.json +37 -14
- package/src/i18n/locales/ja.json +37 -14
- package/src/i18n/locales/ko.json +37 -14
- package/src/i18n/locales/la.json +37 -14
- package/src/i18n/locales/lt.json +37 -14
- package/src/i18n/locales/nl.json +37 -14
- package/src/i18n/locales/pl.json +37 -14
- package/src/i18n/locales/pt.json +37 -14
- package/src/i18n/locales/ru.json +37 -14
- package/src/i18n/locales/se.json +37 -14
- package/src/i18n/locales/th.json +37 -14
- package/src/i18n/locales/tr.json +37 -14
- package/src/i18n/locales/{ua.json → uk.json} +37 -14
- package/src/i18n/locales/vi.json +37 -14
- package/src/types/slashCommandsTypes.ts +19 -0
- package/src/types/toolbarTypes.ts +1 -1
- package/dist/tiptapify.css +0 -1
- package/dist/tiptapify.mjs +0 -82239
- package/dist/tiptapify.umd.js +0 -202
package/README.md
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
# Tiptapify
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/tiptapify)
|
|
4
|
+
[](https://www.npmjs.com/package/tiptapify)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
3
7
|
---
|
|
4
8
|
|
|
5
|
-
[Tiptap](https://tiptap.dev)
|
|
9
|
+
[Tiptap 3 Editor](https://tiptap.dev) [Vuetify](https://vuetifyjs.com) toolbar implementation
|
|
6
10
|
|
|
7
11
|
## Status
|
|
8
|
-
*
|
|
12
|
+
*Beta*
|
|
13
|
+
|
|
14
|
+
## Live Demo
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
[View Documentation & Demo](https://ivoyt.github.io/tiptapify)
|
|
11
17
|
|
|
12
18
|
## Requirements
|
|
13
19
|
- Vue 3.x
|
|
@@ -155,11 +161,11 @@ Found a bug or have ideas on improvement? Feel free to [create a ticket](https:/
|
|
|
155
161
|
- [x] filter option in emoji extension
|
|
156
162
|
- [x] option to provide custom extension
|
|
157
163
|
- [x] iframe extension
|
|
158
|
-
- [
|
|
164
|
+
- [x] charmap extension
|
|
165
|
+
- [x] demo
|
|
166
|
+
- [x] documentation
|
|
159
167
|
- [ ] extended video extensions
|
|
160
168
|
- [ ] print hotkey in a tooltip
|
|
161
|
-
- [ ] demo
|
|
162
|
-
- [ ] documentation
|
|
163
169
|
- [ ] AI features
|
|
164
170
|
|
|
165
171
|
## Licence
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiptapify",
|
|
3
3
|
"types": "./index.d.ts",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"description": "Tiptap3 editor with Vuetify3 menu implementation",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -30,7 +30,11 @@
|
|
|
30
30
|
"dev": "vite",
|
|
31
31
|
"build": "vite build",
|
|
32
32
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
33
|
-
"build:
|
|
33
|
+
"build:charmap": "tsx build-charmap.ts",
|
|
34
|
+
"build:emojis": "tsx build-emojis.ts",
|
|
35
|
+
"docs:dev": "pnpm --filter tiptapify-docs docs:dev",
|
|
36
|
+
"docs:build": "pnpm --filter tiptapify-docs docs:build",
|
|
37
|
+
"docs:preview": "pnpm --filter tiptapify-docs docs:preview"
|
|
34
38
|
},
|
|
35
39
|
"keywords": [
|
|
36
40
|
"vue",
|
|
@@ -57,7 +61,7 @@
|
|
|
57
61
|
"author": "Igor Voytovich",
|
|
58
62
|
"license": "MIT",
|
|
59
63
|
"repository": "https://github.com/IVoyt/tiptapify",
|
|
60
|
-
"packageManager": "pnpm@10.
|
|
64
|
+
"packageManager": "pnpm@10.33.4",
|
|
61
65
|
"dependencies": {
|
|
62
66
|
"@tiptap/core": "^3.20.4",
|
|
63
67
|
"@tiptap/extension-blockquote": "^3.20.4",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import defaults from "@tiptapify/constants/defaults";
|
|
4
4
|
import { itemsPropType, toolbarSections } from "@tiptapify/types/toolbarTypes";
|
|
5
|
+
import { SlashCommandsConfig } from "@tiptapify/types/slashCommandsTypes";
|
|
5
6
|
import { computed, onBeforeUnmount, PropType, provide, ref, ShallowRef, watch } from "vue";
|
|
6
7
|
import { default as Toolbar } from "@tiptapify/components/Toolbar/Index.vue";
|
|
7
8
|
import { Editor, EditorContent } from '@tiptap/vue-3'
|
|
@@ -20,7 +21,7 @@ const { t, i18n, setLocale } = useLocale();
|
|
|
20
21
|
const props = defineProps({
|
|
21
22
|
locale: { type: String, default () { return 'en' } },
|
|
22
23
|
content: String|Object,
|
|
23
|
-
height: { type: Number, default () { return null } },
|
|
24
|
+
height: { type: [Number,String], default () { return null } },
|
|
24
25
|
variantBtn: { type: String, default () { return defaults.variantBtn } },
|
|
25
26
|
variantField: { type: String, default () { return defaults.variantField } },
|
|
26
27
|
toolbar: { type: Boolean, default () { return true } },
|
|
@@ -28,7 +29,7 @@ const props = defineProps({
|
|
|
28
29
|
itemsExclude: { type: Boolean, default() { return false } },
|
|
29
30
|
bubbleMenu: { type: Boolean, default () { return true } },
|
|
30
31
|
floatingMenu: { type: Boolean, default () { return true } },
|
|
31
|
-
slashCommands: { type: Boolean, default () { return true } },
|
|
32
|
+
slashCommands: { type: [Boolean, Array] as PropType<SlashCommandsConfig>, default () { return true } },
|
|
32
33
|
placeholder: { type: String, default () { return '' } },
|
|
33
34
|
showWordsCount: { type: Boolean, default () { return true } },
|
|
34
35
|
showCharactersCount: { type: Boolean, default () { return true } },
|
|
@@ -71,6 +72,7 @@ watch(() => editor.value, (editorInstance) => {
|
|
|
71
72
|
editor.value.interactiveStyles = interactiveStyles.value
|
|
72
73
|
emit('editor-ready', {
|
|
73
74
|
editor: editorInstance,
|
|
75
|
+
setLocale,
|
|
74
76
|
getHTML: () => editorInstance.getHTML(),
|
|
75
77
|
getJSON: () => editorInstance.getJSON(),
|
|
76
78
|
});
|
|
@@ -421,5 +423,96 @@ onBeforeUnmount(() => {
|
|
|
421
423
|
ul.list-style-square {
|
|
422
424
|
list-style-type: square !important;
|
|
423
425
|
}
|
|
426
|
+
|
|
427
|
+
img {
|
|
428
|
+
display: block;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
[data-node="image"].has-focus {
|
|
432
|
+
[data-resize-handle] {
|
|
433
|
+
position: absolute;
|
|
434
|
+
background: rgba(0, 0, 0, 0.5);
|
|
435
|
+
border: 1px solid rgba(255, 255, 255, 0.8);
|
|
436
|
+
border-radius: 2px;
|
|
437
|
+
z-index: 10;
|
|
438
|
+
|
|
439
|
+
&:hover {
|
|
440
|
+
background: rgba(0, 0, 0, 0.8);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* Corner handles */
|
|
444
|
+
&[data-resize-handle='top-left'],
|
|
445
|
+
&[data-resize-handle='top-right'],
|
|
446
|
+
&[data-resize-handle='bottom-left'],
|
|
447
|
+
&[data-resize-handle='bottom-right'] {
|
|
448
|
+
width: 8px;
|
|
449
|
+
height: 8px;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
&[data-resize-handle='top-left'] {
|
|
453
|
+
top: -4px;
|
|
454
|
+
left: -4px;
|
|
455
|
+
cursor: nwse-resize;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
&[data-resize-handle='top-right'] {
|
|
459
|
+
top: -4px;
|
|
460
|
+
right: -4px;
|
|
461
|
+
cursor: nesw-resize;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
&[data-resize-handle='bottom-left'] {
|
|
465
|
+
bottom: -4px;
|
|
466
|
+
left: -4px;
|
|
467
|
+
cursor: nesw-resize;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
&[data-resize-handle='bottom-right'] {
|
|
471
|
+
bottom: -4px;
|
|
472
|
+
right: -4px;
|
|
473
|
+
cursor: nwse-resize;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* Edge handles */
|
|
477
|
+
&[data-resize-handle='top'],
|
|
478
|
+
&[data-resize-handle='bottom'] {
|
|
479
|
+
height: 6px;
|
|
480
|
+
left: 8px;
|
|
481
|
+
right: 8px;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
&[data-resize-handle='top'] {
|
|
485
|
+
top: -3px;
|
|
486
|
+
cursor: ns-resize;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
&[data-resize-handle='bottom'] {
|
|
490
|
+
bottom: -3px;
|
|
491
|
+
cursor: ns-resize;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
&[data-resize-handle='left'],
|
|
495
|
+
&[data-resize-handle='right'] {
|
|
496
|
+
width: 6px;
|
|
497
|
+
top: 8px;
|
|
498
|
+
bottom: 8px;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
&[data-resize-handle='left'] {
|
|
502
|
+
left: -3px;
|
|
503
|
+
cursor: ew-resize;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
&[data-resize-handle='right'] {
|
|
507
|
+
right: -3px;
|
|
508
|
+
cursor: ew-resize;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
[data-resize-state='true'] [data-resize-wrapper] {
|
|
513
|
+
outline: 1px solid rgba(0, 0, 0, 0.25);
|
|
514
|
+
border-radius: 0.125rem;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
424
517
|
}
|
|
425
518
|
</style>
|
|
@@ -54,6 +54,16 @@ function prepareToolbarItems() {
|
|
|
54
54
|
}
|
|
55
55
|
} else if (propsItems.value.length > 0) {
|
|
56
56
|
for (const propsItem of propsItems.value as string[]) {
|
|
57
|
+
if (propsItem === '|') {
|
|
58
|
+
if (typeof _toolbarItems['__separator__'] === 'undefined') {
|
|
59
|
+
_toolbarItems['__separator__'] = {
|
|
60
|
+
section: '__separator__',
|
|
61
|
+
group: false,
|
|
62
|
+
components: []
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
57
67
|
const item = propsItem.split(':')
|
|
58
68
|
if (!availableItemsKeys.includes(item[0])) {
|
|
59
69
|
throw new Error(`The ${propsItem} is unknown extension. Please use one of the following: ${availableItemsKeys.join(', ')}`)
|
|
@@ -15,21 +15,25 @@ defineProps({
|
|
|
15
15
|
<template>
|
|
16
16
|
<VToolbarItems class="py-2">
|
|
17
17
|
<template v-for="item in items" :key="item.section">
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
</template>
|
|
22
|
-
</VBtnGroup>
|
|
18
|
+
<template v-if="item.section === '__separator__'">
|
|
19
|
+
<div class="menu-divider"></div>
|
|
20
|
+
</template>
|
|
23
21
|
<template v-else>
|
|
24
|
-
<
|
|
25
|
-
v-for="sectionItem in item.components"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
<VBtnGroup v-if="item.group" elevation="4">
|
|
23
|
+
<template v-for="sectionItem in item.components" :key="sectionItem.name">
|
|
24
|
+
<component :is="sectionItem.component" v-bind="{ ...sectionItem.props ?? {} }" />
|
|
25
|
+
</template>
|
|
26
|
+
</VBtnGroup>
|
|
27
|
+
<template v-else>
|
|
28
|
+
<component
|
|
29
|
+
v-for="sectionItem in item.components"
|
|
30
|
+
:key="sectionItem.name"
|
|
31
|
+
:is="sectionItem.component"
|
|
32
|
+
v-bind="{ variantBtn, ...sectionItem.props ?? {} }"
|
|
33
|
+
/>
|
|
34
|
+
</template>
|
|
35
|
+
<div class="menu-divider"></div>
|
|
30
36
|
</template>
|
|
31
|
-
|
|
32
|
-
<div class="menu-divider"></div>
|
|
33
37
|
</template>
|
|
34
38
|
</VToolbarItems>
|
|
35
39
|
</template>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as EmojiButton } from "@tiptapify/extensions/components/media/emoji/Button.vue";
|
|
2
|
+
import { default as CharmapButton } from "@tiptapify/extensions/components/media/charmap/Button.vue";
|
|
2
3
|
import { default as LinkButton } from "@tiptapify/extensions/components/media/link/Button.vue";
|
|
3
4
|
import { default as ImageButton } from "@tiptapify/extensions/components/media/image/Button.vue";
|
|
4
5
|
import { default as IframeButton } from "@tiptapify/extensions/components/media/iframe/Button.vue";
|
|
@@ -30,6 +31,10 @@ export default {
|
|
|
30
31
|
name: 'emoji',
|
|
31
32
|
component: markRaw(EmojiButton),
|
|
32
33
|
},
|
|
34
|
+
{
|
|
35
|
+
name: 'charmap',
|
|
36
|
+
component: markRaw(CharmapButton),
|
|
37
|
+
},
|
|
33
38
|
{
|
|
34
39
|
name: 'table',
|
|
35
40
|
component: markRaw(TableButton),
|
|
@@ -31,8 +31,8 @@ import { TiptapifyImage } from '@tiptapify/extensions/components/media/image'
|
|
|
31
31
|
import { TiptapifyLink } from '@tiptapify/extensions/components/media/link'
|
|
32
32
|
import { TiptapifyVideo } from '@tiptapify/extensions/components/media/video'
|
|
33
33
|
import CodeBlockComponent from '@tiptapify/extensions/components/CodeBlockComponent.vue'
|
|
34
|
-
import SlashCommands from '@tiptapify/extensions/slash-commands'
|
|
35
|
-
import
|
|
34
|
+
import SlashCommands, { SlashCommandsExtensionOptions } from '@tiptapify/extensions/slash-commands'
|
|
35
|
+
import { SlashCommandsConfig, SlashCommandId } from '@tiptapify/types/slashCommandsTypes'
|
|
36
36
|
import { toolbarSections } from "@tiptapify/types/toolbarTypes";
|
|
37
37
|
|
|
38
38
|
// load all languages with "all" or common languages with "common"
|
|
@@ -51,7 +51,7 @@ const lowlight = createLowlight(common)
|
|
|
51
51
|
// register language example
|
|
52
52
|
// lowlight.register('ts', ts)
|
|
53
53
|
|
|
54
|
-
export function editorExtensions (placeholder: string, slashCommands:
|
|
54
|
+
export function editorExtensions (placeholder: string, slashCommands: SlashCommandsConfig, customExtensions: toolbarSections) {
|
|
55
55
|
const extensions = [
|
|
56
56
|
TextStyleKit,
|
|
57
57
|
Document,
|
|
@@ -78,7 +78,12 @@ export function editorExtensions (placeholder: string, slashCommands: boolean, c
|
|
|
78
78
|
openOnClick: false,
|
|
79
79
|
defaultProtocol: 'https'
|
|
80
80
|
}),
|
|
81
|
-
Image
|
|
81
|
+
Image.configure({
|
|
82
|
+
resize: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
alwaysPreserveAspectRatio: true,
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
82
87
|
Youtube.configure({
|
|
83
88
|
controls: true,
|
|
84
89
|
nocookie: true,
|
|
@@ -110,8 +115,12 @@ export function editorExtensions (placeholder: string, slashCommands: boolean, c
|
|
|
110
115
|
BulletListSquare
|
|
111
116
|
]
|
|
112
117
|
|
|
113
|
-
if (slashCommands) {
|
|
114
|
-
|
|
118
|
+
if (slashCommands !== false) {
|
|
119
|
+
const config: SlashCommandsExtensionOptions = {}
|
|
120
|
+
if (Array.isArray(slashCommands)) {
|
|
121
|
+
config.suggestion = { allowedCommands: slashCommands }
|
|
122
|
+
}
|
|
123
|
+
extensions.push(SlashCommands.configure(config))
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
if (customExtensions.length) {
|
package/src/components/index.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Editor, useEditor } from "@tiptap/vue-3";
|
|
2
2
|
import { editorExtensions } from "@tiptapify/components/editorExtensions";
|
|
3
3
|
import { toolbarSections } from "@tiptapify/types/toolbarTypes";
|
|
4
|
+
import { SlashCommandsConfig } from "@tiptapify/types/slashCommandsTypes";
|
|
4
5
|
import { ShallowRef } from "vue";
|
|
5
6
|
|
|
6
7
|
export function getTiptapEditor (
|
|
7
8
|
content: any,
|
|
8
9
|
placeholder: string,
|
|
9
|
-
slashCommands:
|
|
10
|
+
slashCommands: SlashCommandsConfig = true,
|
|
10
11
|
customExtensions: toolbarSections,
|
|
11
12
|
onUpdate: Function = () => {}
|
|
12
13
|
): ShallowRef<Editor | undefined> {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { reactive } from 'vue'
|
|
2
|
+
|
|
3
|
+
type PickerCloseEvent = {
|
|
4
|
+
type: 'emoji' | 'charmap'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
type PickerEvents = {
|
|
8
|
+
close: PickerCloseEvent
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type EventCallback<T> = (data: T) => void
|
|
12
|
+
|
|
13
|
+
class PickerEventBusClass {
|
|
14
|
+
private listeners: Map<keyof PickerEvents, Set<EventCallback<any>>> = new Map()
|
|
15
|
+
|
|
16
|
+
on<K extends keyof PickerEvents>(event: K, callback: EventCallback<PickerEvents[K]>) {
|
|
17
|
+
if (!this.listeners.has(event)) {
|
|
18
|
+
this.listeners.set(event, new Set())
|
|
19
|
+
}
|
|
20
|
+
this.listeners.get(event)!.add(callback)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
off<K extends keyof PickerEvents>(event: K, callback: EventCallback<PickerEvents[K]>) {
|
|
24
|
+
this.listeners.get(event)?.delete(callback)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
emit<K extends keyof PickerEvents>(event: K, data: PickerEvents[K]) {
|
|
28
|
+
this.listeners.get(event)?.forEach(callback => callback(data))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const PickerEventBus = reactive(new PickerEventBusClass())
|