@seorii/tiptap 0.1.2 → 0.2.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/dist/plugin/command/emoji.d.ts +20 -0
- package/dist/plugin/command/emoji.js +81 -0
- package/dist/plugin/command/stores.d.ts +1 -0
- package/dist/plugin/command/stores.js +1 -0
- package/dist/plugin/command/suggest.d.ts +6 -1
- package/dist/plugin/command/suggest.js +46 -11
- package/dist/plugin/table/style/grip.scss +1 -1
- package/dist/plugin/table/style/resize.scss +2 -2
- package/dist/plugin/table/style/table.scss +1 -1
- package/dist/plugin/table/style.scss +0 -1
- package/dist/tiptap/Command.svelte +42 -17
- package/dist/tiptap/TipTap.svelte +7 -4
- package/dist/tiptap/tiptap.js +56 -51
- package/package.json +3 -1
- package/dist/plugin/command/index.d.ts +0 -4
- package/dist/plugin/command/index.js +0 -27
- package/dist/plugin/table/style/theme.scss +0 -6
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PluginKey } from "prosemirror-state";
|
|
2
|
+
import type { Editor } from "@tiptap/core";
|
|
3
|
+
export declare const emoji: {
|
|
4
|
+
pluginKey: PluginKey<any>;
|
|
5
|
+
char: string;
|
|
6
|
+
items: ({ query }: {
|
|
7
|
+
query: any;
|
|
8
|
+
}) => {
|
|
9
|
+
title: string;
|
|
10
|
+
command: {};
|
|
11
|
+
}[];
|
|
12
|
+
render: () => {
|
|
13
|
+
onStart: (props: any) => void;
|
|
14
|
+
onUpdate(props: any): void;
|
|
15
|
+
onKeyDown(props: any): true | undefined;
|
|
16
|
+
onExit(): void;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
declare const _default: (editor: Editor) => import("prosemirror-state").Plugin<any>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail } from './stores';
|
|
2
|
+
import { PluginKey } from "prosemirror-state";
|
|
3
|
+
import Suggestion from "@tiptap/suggestion";
|
|
4
|
+
//@ts-ignore
|
|
5
|
+
import emojis from 'emojis-list';
|
|
6
|
+
//@ts-ignore
|
|
7
|
+
import tags from 'emojis-keywords';
|
|
8
|
+
const max = 10;
|
|
9
|
+
function fixRange(editor, range, split = '/') {
|
|
10
|
+
const { state } = editor.view, { selection, doc } = state;
|
|
11
|
+
if (selection.$to.nodeBefore?.text?.includes?.(split)) {
|
|
12
|
+
range.from = range.to;
|
|
13
|
+
while (range.from > 0 && doc.textBetween(range.from - 1, range.from) !== split) {
|
|
14
|
+
try {
|
|
15
|
+
range.from -= 1;
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
range.from += 2;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
range.from -= 1;
|
|
23
|
+
}
|
|
24
|
+
while (range.to < selection.to && doc.textBetween(range.to, range.to + 1) !== ' ') {
|
|
25
|
+
try {
|
|
26
|
+
range.to += 1;
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
range.to -= 1;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return range;
|
|
34
|
+
}
|
|
35
|
+
export const emoji = {
|
|
36
|
+
pluginKey: new PluginKey('slash-emoji'),
|
|
37
|
+
char: ':',
|
|
38
|
+
items: ({ query }) => {
|
|
39
|
+
query = ':' + query.toLowerCase();
|
|
40
|
+
const filtered = [];
|
|
41
|
+
for (let i = 0; i < emojis.length; i++) {
|
|
42
|
+
if (tags[i]?.includes?.(query))
|
|
43
|
+
filtered.push({
|
|
44
|
+
title: emojis[i] + ' ' + tags[i],
|
|
45
|
+
command: ({ editor, range }) => {
|
|
46
|
+
editor.chain().deleteRange(fixRange(editor, range, ':')).insertContent(emojis[i]).run();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (filtered.length >= max)
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
return filtered;
|
|
53
|
+
},
|
|
54
|
+
render: () => {
|
|
55
|
+
return {
|
|
56
|
+
onStart: (props) => {
|
|
57
|
+
let editor = props.editor;
|
|
58
|
+
let range = props.range;
|
|
59
|
+
let location = props.clientRect();
|
|
60
|
+
slashProps.set({ editor, range });
|
|
61
|
+
slashVisible.set(true);
|
|
62
|
+
slashLocaltion.set({ x: location.x, y: location.y, height: location.height });
|
|
63
|
+
slashItems.set(props.items);
|
|
64
|
+
slashDetail.set('emoji');
|
|
65
|
+
},
|
|
66
|
+
onUpdate(props) {
|
|
67
|
+
slashItems.set(props.items);
|
|
68
|
+
},
|
|
69
|
+
onKeyDown(props) {
|
|
70
|
+
if (props.event.key === 'Escape') {
|
|
71
|
+
slashVisible.set(false);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
onExit() {
|
|
76
|
+
slashVisible.set(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
export default (editor) => Suggestion({ ...emoji, editor });
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { PluginKey } from "prosemirror-state";
|
|
2
|
+
import { Editor } from "@tiptap/core";
|
|
3
|
+
export declare const suggest: {
|
|
4
|
+
pluginKey: PluginKey<any>;
|
|
5
|
+
char: string;
|
|
2
6
|
items: ({ query }: {
|
|
3
7
|
query: any;
|
|
4
8
|
}) => {
|
|
@@ -20,4 +24,5 @@ declare const _default: {
|
|
|
20
24
|
onExit(): void;
|
|
21
25
|
};
|
|
22
26
|
};
|
|
27
|
+
declare const _default: (editor: Editor) => import("prosemirror-state").Plugin<any>;
|
|
23
28
|
export default _default;
|
|
@@ -1,7 +1,38 @@
|
|
|
1
|
-
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail } from './stores';
|
|
1
|
+
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail, slashSelection } from './stores';
|
|
2
2
|
import i18n from "../../i18n";
|
|
3
3
|
import { fallbackUpload } from "../image/dragdrop";
|
|
4
|
-
|
|
4
|
+
import { PluginKey } from "prosemirror-state";
|
|
5
|
+
import { Editor } from "@tiptap/core";
|
|
6
|
+
import Suggestion from "@tiptap/suggestion";
|
|
7
|
+
function fixRange(editor, range, split = '/') {
|
|
8
|
+
const { state } = editor.view, { selection, doc } = state;
|
|
9
|
+
if (selection.$to.nodeBefore?.text?.includes?.(split)) {
|
|
10
|
+
range.from = range.to;
|
|
11
|
+
while (range.from > 0 && doc.textBetween(range.from - 1, range.from) !== split) {
|
|
12
|
+
try {
|
|
13
|
+
range.from -= 1;
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
range.from += 2;
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
range.from -= 1;
|
|
21
|
+
}
|
|
22
|
+
while (range.to < selection.to && doc.textBetween(range.to, range.to + 1) !== ' ') {
|
|
23
|
+
try {
|
|
24
|
+
range.to += 1;
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
range.to -= 1;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return range;
|
|
32
|
+
}
|
|
33
|
+
export const suggest = {
|
|
34
|
+
pluginKey: new PluginKey('slash-suggest'),
|
|
35
|
+
char: '/',
|
|
5
36
|
items: ({ query }) => {
|
|
6
37
|
const raw = [
|
|
7
38
|
{
|
|
@@ -11,7 +42,7 @@ export default {
|
|
|
11
42
|
title: i18n('title') + ' 1',
|
|
12
43
|
subtitle: i18n('title1Info'),
|
|
13
44
|
command: ({ editor, range }) => {
|
|
14
|
-
editor.chain().focus().deleteRange(range).setNode('heading', { level: 1 }).run();
|
|
45
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).setNode('heading', { level: 1 }).run();
|
|
15
46
|
}
|
|
16
47
|
},
|
|
17
48
|
{
|
|
@@ -19,7 +50,7 @@ export default {
|
|
|
19
50
|
title: i18n('title') + ' 2',
|
|
20
51
|
subtitle: i18n('title2Info'),
|
|
21
52
|
command: ({ editor, range }) => {
|
|
22
|
-
editor.chain().focus().deleteRange(range).setNode('heading', { level: 2 }).run();
|
|
53
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).setNode('heading', { level: 2 }).run();
|
|
23
54
|
}
|
|
24
55
|
},
|
|
25
56
|
{
|
|
@@ -27,7 +58,7 @@ export default {
|
|
|
27
58
|
title: i18n('title') + ' 3',
|
|
28
59
|
subtitle: i18n('title3Info'),
|
|
29
60
|
command: ({ editor, range }) => {
|
|
30
|
-
editor.chain().focus().deleteRange(range).setNode('heading', { level: 3 }).run();
|
|
61
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).setNode('heading', { level: 3 }).run();
|
|
31
62
|
}
|
|
32
63
|
},
|
|
33
64
|
{
|
|
@@ -35,7 +66,7 @@ export default {
|
|
|
35
66
|
title: i18n('unorderedList'),
|
|
36
67
|
subtitle: i18n('unorderedListInfo'),
|
|
37
68
|
command: ({ editor, range }) => {
|
|
38
|
-
editor.commands.deleteRange(range);
|
|
69
|
+
editor.commands.deleteRange(fixRange(editor, range));
|
|
39
70
|
editor.commands.toggleBulletList();
|
|
40
71
|
}
|
|
41
72
|
},
|
|
@@ -44,7 +75,7 @@ export default {
|
|
|
44
75
|
title: i18n('numberList'),
|
|
45
76
|
subtitle: i18n('numberListInfo'),
|
|
46
77
|
command: ({ editor, range }) => {
|
|
47
|
-
editor.commands.deleteRange(range);
|
|
78
|
+
editor.commands.deleteRange(fixRange(editor, range));
|
|
48
79
|
editor.commands.toggleOrderedList();
|
|
49
80
|
}
|
|
50
81
|
}
|
|
@@ -57,6 +88,7 @@ export default {
|
|
|
57
88
|
title: i18n('image'),
|
|
58
89
|
subtitle: i18n('imageInfo'),
|
|
59
90
|
command: ({ editor, range }) => {
|
|
91
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).run();
|
|
60
92
|
const input = document.createElement('input');
|
|
61
93
|
input.type = 'file';
|
|
62
94
|
input.accept = 'image/*';
|
|
@@ -78,7 +110,7 @@ export default {
|
|
|
78
110
|
title: i18n('codeBlock'),
|
|
79
111
|
subtitle: i18n('codeBlockInfo'),
|
|
80
112
|
command: ({ editor, range }) => {
|
|
81
|
-
editor.chain().focus().deleteRange(range).setNode('codeBlock').run();
|
|
113
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).setNode('codeBlock').run();
|
|
82
114
|
}
|
|
83
115
|
},
|
|
84
116
|
{
|
|
@@ -87,7 +119,7 @@ export default {
|
|
|
87
119
|
subtitle: i18n('mathBlockInfo'),
|
|
88
120
|
command: ({ editor, range }) => {
|
|
89
121
|
const { to } = range;
|
|
90
|
-
editor.chain().focus().deleteRange(range).setNode('math_display').focus().run();
|
|
122
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).setNode('math_display').focus().run();
|
|
91
123
|
}
|
|
92
124
|
},
|
|
93
125
|
{
|
|
@@ -95,7 +127,7 @@ export default {
|
|
|
95
127
|
title: i18n('table'),
|
|
96
128
|
subtitle: i18n('tableInfo'),
|
|
97
129
|
command: ({ editor, range }) => {
|
|
98
|
-
editor.chain().focus().insertTable({ rows: 2, cols: 3 }).run();
|
|
130
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).insertTable({ rows: 2, cols: 3 }).run();
|
|
99
131
|
}
|
|
100
132
|
},
|
|
101
133
|
{
|
|
@@ -103,7 +135,7 @@ export default {
|
|
|
103
135
|
title: i18n('blockquote'),
|
|
104
136
|
subtitle: i18n('blockquoteInfo'),
|
|
105
137
|
command: ({ editor, range }) => {
|
|
106
|
-
editor.chain().focus().deleteRange(range).setBlockquote().focus().run();
|
|
138
|
+
editor.chain().focus().deleteRange(fixRange(editor, range)).setBlockquote().focus().run();
|
|
107
139
|
}
|
|
108
140
|
},
|
|
109
141
|
{
|
|
@@ -111,6 +143,7 @@ export default {
|
|
|
111
143
|
title: i18n('iframe'),
|
|
112
144
|
subtitle: i18n('iframeInfo'),
|
|
113
145
|
command: ({ editor, range }) => {
|
|
146
|
+
slashSelection.set(() => editor.chain().focus().deleteRange(fixRange(editor, range)).run());
|
|
114
147
|
slashDetail.set('iframe');
|
|
115
148
|
}
|
|
116
149
|
},
|
|
@@ -119,6 +152,7 @@ export default {
|
|
|
119
152
|
title: i18n('youtube'),
|
|
120
153
|
subtitle: i18n('youtubeInfo'),
|
|
121
154
|
command: ({ editor, range }) => {
|
|
155
|
+
slashSelection.set(() => editor.chain().focus().deleteRange(fixRange(editor, range)).run());
|
|
122
156
|
slashDetail.set('youtube');
|
|
123
157
|
}
|
|
124
158
|
}
|
|
@@ -158,3 +192,4 @@ export default {
|
|
|
158
192
|
};
|
|
159
193
|
}
|
|
160
194
|
};
|
|
195
|
+
export default (editor) => Suggestion({ ...suggest, editor });
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
background-color: var(--primary-light6);
|
|
10
10
|
opacity: 0;
|
|
11
11
|
|
|
12
|
-
.
|
|
12
|
+
.editable & {
|
|
13
13
|
opacity: 1;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
&.resize-cursor {
|
|
18
18
|
pointer-events: none;
|
|
19
19
|
|
|
20
|
-
.
|
|
20
|
+
.editable & {
|
|
21
21
|
pointer-events: initial;
|
|
22
22
|
cursor: ew-resize;
|
|
23
23
|
cursor: col-resize; /* stylelint-disable declaration-block-no-duplicate-properties */
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
<script>import { Button, IconButton, Input, List, TwoLine } from "nunui";
|
|
1
|
+
<script>import { Button, IconButton, Input, List, OneLine, TwoLine } from "nunui";
|
|
2
2
|
import { getContext } from "svelte";
|
|
3
|
-
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail } from '../plugin/command/stores';
|
|
3
|
+
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail, slashSelection } from '../plugin/command/stores';
|
|
4
4
|
import { fly, slide } from "svelte/transition";
|
|
5
5
|
import { quartOut } from "svelte/easing";
|
|
6
6
|
import i18n from "../i18n";
|
|
7
7
|
const tiptap = getContext('editor');
|
|
8
8
|
export let selectedIndex = 0;
|
|
9
9
|
let height = 0, elements = [];
|
|
10
|
-
let iframe = '';
|
|
10
|
+
let iframe = '', focus;
|
|
11
11
|
$: if ($slashVisible) {
|
|
12
12
|
iframe = '';
|
|
13
13
|
}
|
|
14
|
+
$: setTimeout(() => focus?.focus?.(), 100);
|
|
14
15
|
</script>
|
|
15
16
|
|
|
16
17
|
<svelte:window bind:innerHeight={height}/>
|
|
@@ -26,16 +27,19 @@ $: if ($slashVisible) {
|
|
|
26
27
|
<IconButton icon="arrow_back" on:click={() => $slashDetail = ''}/>
|
|
27
28
|
<div class="title">iframe</div>
|
|
28
29
|
</header>
|
|
29
|
-
<Input placeholder="url" fullWidth bind:value={iframe}
|
|
30
|
+
<Input placeholder="url" fullWidth bind:value={iframe} bind:input={focus}
|
|
30
31
|
on:submit={() => $tiptap.commands.insertContent({type: 'iframe', attrs: {src: iframe}})}/>
|
|
31
32
|
<footer>
|
|
32
33
|
<Button tabindex="0" transparent small on:click={() => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
iframe = ''
|
|
35
|
+
$slashDetail = ''
|
|
36
|
+
}}>{i18n('cancel')}
|
|
36
37
|
</Button>
|
|
37
38
|
<Button tabindex="0" transparent small
|
|
38
|
-
on:click={() =>
|
|
39
|
+
on:click={() => {
|
|
40
|
+
$slashSelection?.();
|
|
41
|
+
$tiptap.commands.insertContent({type: 'iframe', attrs: {src: iframe}})}
|
|
42
|
+
}>{i18n('insert')}
|
|
39
43
|
</Button>
|
|
40
44
|
</footer>
|
|
41
45
|
</div>
|
|
@@ -45,31 +49,52 @@ $: if ($slashVisible) {
|
|
|
45
49
|
<IconButton icon="arrow_back" on:click={() => $slashDetail = ''}/>
|
|
46
50
|
<div class="title">Youtube</div>
|
|
47
51
|
</header>
|
|
48
|
-
<Input placeholder="url" fullWidth bind:value={iframe}
|
|
52
|
+
<Input placeholder="url" fullWidth bind:value={iframe} bind:input={focus}
|
|
49
53
|
on:submit={() => $tiptap.commands.insertVideoPlayer({url: iframe})}/>
|
|
50
54
|
<footer>
|
|
51
55
|
<Button tabindex="0" transparent small on:click={() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
iframe = ''
|
|
57
|
+
$slashDetail = ''
|
|
58
|
+
}}>{i18n('cancel')}
|
|
55
59
|
</Button>
|
|
56
|
-
<Button tabindex="0" transparent small
|
|
57
|
-
|
|
60
|
+
<Button tabindex="0" transparent small on:click={() => {
|
|
61
|
+
$slashSelection?.();
|
|
62
|
+
$tiptap.commands.insertVideoPlayer({url: iframe});
|
|
63
|
+
}}>{i18n('insert')}
|
|
58
64
|
</Button>
|
|
59
65
|
</footer>
|
|
60
66
|
</div>
|
|
67
|
+
{:else if $slashDetail === 'emoji'}
|
|
68
|
+
<div class="list">
|
|
69
|
+
<List>
|
|
70
|
+
{#each $slashItems as {title, command}, i(title)}
|
|
71
|
+
<div transition:slide={{duration: 400, easing: quartOut}}>
|
|
72
|
+
<OneLine on:click={() => {
|
|
73
|
+
command?.($slashProps);
|
|
74
|
+
setTimeout(() => $tiptap.commands.focus());
|
|
75
|
+
}} bind:this={elements[i]} {title} active={selectedIndex === i}/>
|
|
76
|
+
</div>
|
|
77
|
+
{/each}
|
|
78
|
+
{#if !$slashItems.length}
|
|
79
|
+
<div class="section"
|
|
80
|
+
transition:slide={{duration: 400, easing: quartOut}}>{i18n('noResult')}</div>
|
|
81
|
+
{/if}
|
|
82
|
+
</List>
|
|
83
|
+
</div>
|
|
61
84
|
{:else}
|
|
62
85
|
<div class="list">
|
|
63
86
|
<List>
|
|
64
|
-
{#each $slashItems as {section, list}(section)}
|
|
87
|
+
{#each $slashItems as {section, list}, j(section)}
|
|
88
|
+
{@const lastCount = $slashItems.slice(0, j).reduce((acc, cur) => acc + cur.list.length, 0)}
|
|
65
89
|
<div class="section" transition:slide={{duration: 400, easing: quartOut}}>{section}</div>
|
|
66
90
|
<div transition:slide={{duration: 400, easing: quartOut}}>
|
|
67
91
|
{#each list || [] as {title, subtitle, icon, command, section}, i(title)}
|
|
68
92
|
<div transition:slide={{duration: 400, easing: quartOut}}>
|
|
69
|
-
<TwoLine on:mouseenter={() => (selectedIndex = i)} on:click={() => {
|
|
93
|
+
<TwoLine on:mouseenter={() => (selectedIndex = i + lastCount)} on:click={() => {
|
|
70
94
|
command?.($slashProps);
|
|
71
95
|
setTimeout(() => $tiptap.commands.focus());
|
|
72
|
-
}} bind:this={elements[i]} {icon} {title} subtitle={subtitle || ''}
|
|
96
|
+
}} bind:this={elements[i + lastCount]} {icon} {title} subtitle={subtitle || ''}
|
|
97
|
+
active={selectedIndex === i + lastCount}/>
|
|
73
98
|
</div>
|
|
74
99
|
{/each}
|
|
75
100
|
</div>
|
|
@@ -64,14 +64,17 @@ $: selectedIndex = $slashVisible ? selectedIndex : 0;
|
|
|
64
64
|
function handleKeydown(event) {
|
|
65
65
|
if (!$slashVisible)
|
|
66
66
|
return;
|
|
67
|
+
let count = $slashItems.length;
|
|
68
|
+
if ($slashItems[0]?.list)
|
|
69
|
+
count = $slashItems.reduce((acc, item) => acc + item.list.length, 0);
|
|
67
70
|
if (event.key === 'ArrowUp') {
|
|
68
71
|
event.preventDefault();
|
|
69
|
-
selectedIndex = (selectedIndex +
|
|
72
|
+
selectedIndex = (selectedIndex + count - 1) % count;
|
|
70
73
|
return true;
|
|
71
74
|
}
|
|
72
75
|
if (event.key === 'ArrowDown') {
|
|
73
76
|
event.preventDefault();
|
|
74
|
-
selectedIndex = (selectedIndex + 1) %
|
|
77
|
+
selectedIndex = (selectedIndex + 1) % count;
|
|
75
78
|
return true;
|
|
76
79
|
}
|
|
77
80
|
if (event.key === 'Enter') {
|
|
@@ -82,10 +85,10 @@ function handleKeydown(event) {
|
|
|
82
85
|
return false;
|
|
83
86
|
}
|
|
84
87
|
function selectItem(index) {
|
|
85
|
-
const item = $slashItems[index];
|
|
88
|
+
const item = $slashItems[0]?.list ? $slashItems.map(i => i.list).flat()[index] : $slashItems[index];
|
|
86
89
|
if (item) {
|
|
87
90
|
let range = $slashProps.range;
|
|
88
|
-
item.command({ editor:
|
|
91
|
+
item.command({ editor: $tiptap, range });
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
</script>
|
package/dist/tiptap/tiptap.js
CHANGED
|
@@ -23,55 +23,60 @@ import Iframe from "../plugin/iframe";
|
|
|
23
23
|
// @ts-ignore
|
|
24
24
|
import { MathInline, MathBlock } from "@seorii/prosemirror-math/tiptap";
|
|
25
25
|
import Youtube from "../plugin/youtube";
|
|
26
|
-
import command from "../plugin/command";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
import command from "../plugin/command/suggest";
|
|
27
|
+
import emoji from "../plugin/command/emoji";
|
|
28
|
+
export default (element, content, { plugins = [], ...props } = {}) => {
|
|
29
|
+
const tt = new Editor({
|
|
30
|
+
element, content, ...props,
|
|
31
|
+
extensions: [
|
|
32
|
+
CodeBlockLowlight.extend({
|
|
33
|
+
addKeyboardShortcuts() {
|
|
34
|
+
return {
|
|
35
|
+
...this.parent?.(),
|
|
36
|
+
'Tab': () => {
|
|
37
|
+
if (this.editor.isActive('codeBlock')) {
|
|
38
|
+
return this.editor.commands.insertContent(' ');
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}).configure({ lowlight }),
|
|
45
|
+
Image,
|
|
46
|
+
Youtube,
|
|
47
|
+
StarterKit,
|
|
48
|
+
Underline,
|
|
49
|
+
Highlight.configure({ multicolor: true }),
|
|
50
|
+
Link.configure({
|
|
51
|
+
openOnClick: true, protocols: ['ftp', 'mailto', {
|
|
52
|
+
scheme: 'tel',
|
|
53
|
+
optionalSlashes: true
|
|
54
|
+
}]
|
|
55
|
+
}),
|
|
56
|
+
TextAlign.configure({ types: ['heading', 'paragraph', 'image'] }),
|
|
57
|
+
DropCursor,
|
|
58
|
+
orderedlist,
|
|
59
|
+
MathInline,
|
|
60
|
+
MathBlock,
|
|
61
|
+
table,
|
|
62
|
+
tableHeader,
|
|
63
|
+
tableRow,
|
|
64
|
+
tableCell,
|
|
65
|
+
Superscript,
|
|
66
|
+
Subscript,
|
|
67
|
+
Indent,
|
|
68
|
+
Color,
|
|
69
|
+
TextStyle,
|
|
70
|
+
Iframe,
|
|
71
|
+
Code.extend({
|
|
72
|
+
renderHTML({ HTMLAttributes }) {
|
|
73
|
+
return ['code', mergeAttributes(HTMLAttributes, { class: 'inline' })];
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
...plugins,
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
tt.registerPlugin(emoji(tt));
|
|
80
|
+
tt.registerPlugin(command(tt));
|
|
81
|
+
return tt;
|
|
82
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seorii/tiptap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "svelte-kit sync && svelte-package",
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
"@tiptap/pm": "^2.0.4",
|
|
61
61
|
"@tiptap/starter-kit": "^2.0.4",
|
|
62
62
|
"@tiptap/suggestion": "^2.0.4",
|
|
63
|
+
"emojis-keywords": "2.0.0",
|
|
64
|
+
"emojis-list": "2.0.0",
|
|
63
65
|
"lowlight": "^2.9.0",
|
|
64
66
|
"nunui": "^0.0.101",
|
|
65
67
|
"prosemirror-commands": "^1.5.2",
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Extension } from '@tiptap/core';
|
|
2
|
-
import Suggestion from '@tiptap/suggestion';
|
|
3
|
-
import suggestion from "./suggest";
|
|
4
|
-
export const Command = Extension.create({
|
|
5
|
-
name: 'slash',
|
|
6
|
-
addOptions() {
|
|
7
|
-
return {
|
|
8
|
-
suggestion: {
|
|
9
|
-
char: '/',
|
|
10
|
-
command: ({ editor, range, props }) => {
|
|
11
|
-
props.command({ editor, range });
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
},
|
|
16
|
-
addProseMirrorPlugins() {
|
|
17
|
-
return [
|
|
18
|
-
Suggestion({
|
|
19
|
-
editor: this.editor,
|
|
20
|
-
...this.options.suggestion
|
|
21
|
-
})
|
|
22
|
-
];
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
export default Command.configure({
|
|
26
|
-
suggestion
|
|
27
|
-
});
|