tiptapify 0.0.36 → 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 +11 -5
- package/package.json +6 -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/editorExtensions.ts +15 -6
- package/src/components/index.ts +2 -1
- package/src/extensions/PickerEventBus.ts +32 -0
- package/src/extensions/components/media/charmap/Button.vue +5 -143
- package/src/extensions/components/media/charmap/Picker.vue +229 -0
- package/src/extensions/components/media/emoji/Button.vue +5 -141
- 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 +3 -2
- package/src/i18n/locales/ch.json +3 -2
- package/src/i18n/locales/cz.json +3 -2
- package/src/i18n/locales/de.json +3 -2
- package/src/i18n/locales/es.json +3 -2
- package/src/i18n/locales/fi.json +3 -2
- package/src/i18n/locales/fr.json +3 -2
- package/src/i18n/locales/hu.json +3 -2
- package/src/i18n/locales/it.json +3 -2
- package/src/i18n/locales/ja.json +3 -2
- package/src/i18n/locales/ko.json +3 -2
- package/src/i18n/locales/la.json +3 -2
- package/src/i18n/locales/lt.json +3 -2
- package/src/i18n/locales/nl.json +3 -2
- package/src/i18n/locales/pl.json +3 -2
- package/src/i18n/locales/pt.json +3 -2
- package/src/i18n/locales/ru.json +3 -2
- package/src/i18n/locales/se.json +3 -2
- package/src/i18n/locales/th.json +3 -2
- package/src/i18n/locales/tr.json +3 -2
- package/src/i18n/locales/{ua.json → uk.json} +3 -2
- package/src/i18n/locales/vi.json +3 -2
- 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 -91388
- 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
|
|
@@ -156,10 +162,10 @@ Found a bug or have ideas on improvement? Feel free to [create a ticket](https:/
|
|
|
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
|
".": {
|
|
@@ -31,7 +31,10 @@
|
|
|
31
31
|
"build": "vite build",
|
|
32
32
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
33
33
|
"build:charmap": "tsx build-charmap.ts",
|
|
34
|
-
"build:emojis": "tsx build-emojis.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"
|
|
35
38
|
},
|
|
36
39
|
"keywords": [
|
|
37
40
|
"vue",
|
|
@@ -58,7 +61,7 @@
|
|
|
58
61
|
"author": "Igor Voytovich",
|
|
59
62
|
"license": "MIT",
|
|
60
63
|
"repository": "https://github.com/IVoyt/tiptapify",
|
|
61
|
-
"packageManager": "pnpm@10.
|
|
64
|
+
"packageManager": "pnpm@10.33.4",
|
|
62
65
|
"dependencies": {
|
|
63
66
|
"@tiptap/core": "^3.20.4",
|
|
64
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>
|
|
@@ -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())
|
|
@@ -1,72 +1,18 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { Editor } from '@tiptap/vue-3'
|
|
3
|
-
import tiptapifyCharMap from "@tiptapify/extensions/charmap"
|
|
4
|
-
import { computed, inject, Ref, ref, watch } from 'vue'
|
|
5
2
|
import * as mdi from '@mdi/js'
|
|
3
|
+
import { inject, Ref } from 'vue'
|
|
4
|
+
import CharmapPicker from './Picker.vue';
|
|
6
5
|
|
|
7
6
|
defineProps({
|
|
8
7
|
variantBtn: { type: String, default: 'flat' },
|
|
9
8
|
})
|
|
10
9
|
|
|
11
|
-
const editor = inject('tiptapifyEditor') as Ref<
|
|
10
|
+
const editor = inject('tiptapifyEditor') as Ref<any>
|
|
12
11
|
const { t } = inject('tiptapifyI18n') as any
|
|
13
|
-
|
|
14
|
-
const filter = ref(null)
|
|
15
|
-
const tab = ref('punctuation')
|
|
16
|
-
const sorted = tiptapifyCharMap.sort((previous, current) => {
|
|
17
|
-
if (previous.order < current.order) {
|
|
18
|
-
return -1
|
|
19
|
-
}
|
|
20
|
-
if (previous.order > current.order) {
|
|
21
|
-
return 1
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return 0
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
if (!tab.value && sorted.length > 0) {
|
|
28
|
-
tab.value = sorted[0].name
|
|
29
|
-
}
|
|
30
|
-
const chars = computed(() => sorted.map(item => { return { group: item.name, emojis: item.items } }))
|
|
31
|
-
const charMapRef = ref(JSON.parse(JSON.stringify(chars.value)))
|
|
32
|
-
|
|
33
|
-
const handleCharacterClick = (charItem: any) => {
|
|
34
|
-
editor.value.chain().focus().insertContent(charItem.char).run()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const filterChars = (filterValue: string) => {
|
|
38
|
-
resetFilter()
|
|
39
|
-
if (!filterValue) {
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const filtered: any[] = []
|
|
44
|
-
|
|
45
|
-
const groupItems = charMapRef.value.find((item: any) => item.group === tab.value)
|
|
46
|
-
if (groupItems?.emojis) {
|
|
47
|
-
groupItems.emojis.forEach((item: any) => {
|
|
48
|
-
if (item.name.toLowerCase().match(filterValue)) {
|
|
49
|
-
filtered.push(item)
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
groupItems.emojis = filtered
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const resetFilter = () => {
|
|
58
|
-
charMapRef.value = JSON.parse(JSON.stringify(chars.value))
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
watch(() => tab.value, () => {
|
|
62
|
-
resetFilter()
|
|
63
|
-
filter.value = null
|
|
64
|
-
})
|
|
65
|
-
|
|
66
12
|
</script>
|
|
67
13
|
|
|
68
14
|
<template>
|
|
69
|
-
<VBtn :id="`tiptapify-charmap-button-${editor.instanceId}`" :variant="variantBtn" size="32"
|
|
15
|
+
<VBtn :id="`tiptapify-charmap-button-${editor.instanceId}`" :variant="variantBtn" size="32">
|
|
70
16
|
<VTooltip activator="parent">
|
|
71
17
|
{{ t('media.charmap.title') }}
|
|
72
18
|
</VTooltip>
|
|
@@ -75,91 +21,7 @@ watch(() => tab.value, () => {
|
|
|
75
21
|
|
|
76
22
|
<VMenu :activator="`#tiptapify-charmap-button-${editor.instanceId}`" :close-on-content-click="false">
|
|
77
23
|
<VSheet class="pa-2" max-width="580">
|
|
78
|
-
<
|
|
79
|
-
<VTabs v-model="tab" mandatory direction="vertical" color="primary" density="compact">
|
|
80
|
-
<VTab
|
|
81
|
-
v-for="item of charMapRef"
|
|
82
|
-
:text="item.group"
|
|
83
|
-
:value="item.group"
|
|
84
|
-
:key="item.group"
|
|
85
|
-
density="compact"
|
|
86
|
-
rounded
|
|
87
|
-
size="small"
|
|
88
|
-
>
|
|
89
|
-
{{ t(`media.charmap.categories.${item.group}`) }}
|
|
90
|
-
</VTab>
|
|
91
|
-
</VTabs>
|
|
92
|
-
|
|
93
|
-
<div class="d-flex flex-column">
|
|
94
|
-
<VTextField
|
|
95
|
-
v-model="filter"
|
|
96
|
-
:label="t('media.charmap.search')"
|
|
97
|
-
density="compact"
|
|
98
|
-
variant="solo"
|
|
99
|
-
:prepend-inner-icon="`mdiSvg:${mdi.mdiMagnify}`"
|
|
100
|
-
class="mb-2 tiptapify-charmap-search"
|
|
101
|
-
hide-details
|
|
102
|
-
clearable
|
|
103
|
-
@update:model-value="filterChars($event)"
|
|
104
|
-
@click:clear="resetFilter"
|
|
105
|
-
/>
|
|
106
|
-
<div class="tiptapify-charmap-container">
|
|
107
|
-
<VWindow v-model="tab" direction="vertical">
|
|
108
|
-
<VWindowItem v-for="item of charMapRef" :value="item.group" :transition="false" :reverse-transition="false">
|
|
109
|
-
<div
|
|
110
|
-
v-for="charItem in item.emojis"
|
|
111
|
-
class="tiptapify-charmap-container-item"
|
|
112
|
-
@click="handleCharacterClick(charItem)"
|
|
113
|
-
:title="charItem.name"
|
|
114
|
-
>
|
|
115
|
-
<div class="tiptapify-charmap-container-item__overlay">
|
|
116
|
-
<span>
|
|
117
|
-
{{ charItem.char }}
|
|
118
|
-
</span>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
</VWindowItem>
|
|
122
|
-
</VWindow>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
24
|
+
<CharmapPicker :editor="editor" :t="t" />
|
|
126
25
|
</VSheet>
|
|
127
26
|
</VMenu>
|
|
128
27
|
</template>
|
|
129
|
-
|
|
130
|
-
<style scoped lang="scss">
|
|
131
|
-
.tiptapify-charmap-container {
|
|
132
|
-
width: 360px;
|
|
133
|
-
height: 550px;
|
|
134
|
-
overflow-y: auto;
|
|
135
|
-
|
|
136
|
-
border: 1px solid rgba(var(--v-theme-on-background), calc(var(--v-border-opacity)));
|
|
137
|
-
border-radius: 8px;
|
|
138
|
-
box-shadow: 0 0 5px rgba(var(--v-theme-on-background), calc(var(--v-border-opacity))) inset;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.tiptapify-charmap-container-item__overlay {
|
|
142
|
-
display: flex;
|
|
143
|
-
justify-content: center;
|
|
144
|
-
align-items: center;
|
|
145
|
-
width: 32px;
|
|
146
|
-
height: 32px;
|
|
147
|
-
border-radius: 5px;
|
|
148
|
-
z-index: 1;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.tiptapify-charmap-container-item {
|
|
152
|
-
position: relative;
|
|
153
|
-
display: inline-flex;
|
|
154
|
-
justify-content: center;
|
|
155
|
-
align-items: center;
|
|
156
|
-
cursor: pointer;
|
|
157
|
-
width: 32px;
|
|
158
|
-
height: 32px;
|
|
159
|
-
transition: background-color 0.2s ease-in-out;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.tiptapify-charmap-container-item:hover .tiptapify-emoji-container-item__overlay {
|
|
163
|
-
background-color: rgba(var(--v-theme-on-background), calc(var(--v-hover-opacity) * var(--v-theme-overlay-multiplier)));
|
|
164
|
-
}
|
|
165
|
-
</style>
|