@seorii/tiptap 0.0.9 → 0.0.10
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/stores.d.ts +1 -0
- package/dist/plugin/command/stores.js +1 -0
- package/dist/plugin/command/suggest.js +18 -1
- package/dist/plugin/youtube.d.ts +15 -0
- package/dist/plugin/youtube.js +100 -0
- package/dist/tiptap/Bubble.svelte +71 -5
- package/dist/tiptap/Command.svelte +89 -21
- package/dist/tiptap/Floating.svelte +4 -4
- package/dist/tiptap/TipTap.svelte +5 -4
- package/dist/tiptap/tiptap.js +8 -1
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { slashVisible, slashItems, slashLocaltion, slashProps } from './stores';
|
|
1
|
+
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail } from './stores';
|
|
2
2
|
export default {
|
|
3
3
|
items: ({ query }) => {
|
|
4
4
|
const raw = [
|
|
@@ -74,6 +74,22 @@ export default {
|
|
|
74
74
|
command: ({ editor, range }) => {
|
|
75
75
|
editor.chain().focus().insertTable({ rows: 2, cols: 3 }).run();
|
|
76
76
|
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
icon: 'iframe',
|
|
80
|
+
title: '프레임',
|
|
81
|
+
subtitle: '다른 웹사이트 삽입',
|
|
82
|
+
command: ({ editor, range }) => {
|
|
83
|
+
slashDetail.set('iframe');
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
icon: 'youtube_activity',
|
|
88
|
+
title: '유튜브',
|
|
89
|
+
subtitle: '유튜브 임베드',
|
|
90
|
+
command: ({ editor, range }) => {
|
|
91
|
+
slashDetail.set('youtube');
|
|
92
|
+
}
|
|
77
93
|
}
|
|
78
94
|
]
|
|
79
95
|
}
|
|
@@ -91,6 +107,7 @@ export default {
|
|
|
91
107
|
slashVisible.set(true);
|
|
92
108
|
slashLocaltion.set({ x: location.x, y: location.y, height: location.height });
|
|
93
109
|
slashItems.set(props.items);
|
|
110
|
+
slashDetail.set(null);
|
|
94
111
|
},
|
|
95
112
|
onUpdate(props) {
|
|
96
113
|
slashItems.set(props.items);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Node } from "@tiptap/core";
|
|
2
|
+
export interface VideoPlayerOptions {
|
|
3
|
+
HTMLAttributes: Record<string, any>;
|
|
4
|
+
}
|
|
5
|
+
declare module "@tiptap/core" {
|
|
6
|
+
interface Commands<ReturnType> {
|
|
7
|
+
videoPlayer: {
|
|
8
|
+
insertVideoPlayer: (options: {
|
|
9
|
+
url: string;
|
|
10
|
+
}) => ReturnType;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
declare const _default: Node<VideoPlayerOptions, any>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Node, mergeAttributes } from "@tiptap/core";
|
|
2
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
3
|
+
const youtubeRegExp = /^(?:(?:https?:)?\/\/)?(?:www\.)?(?:m\.)?(?:youtu(?:be)?\.com\/(?:v\/|embed\/|watch(?:\/|\?v=))|youtu\.be\/)((?:\w|-){11})(?:\S+)?$/;
|
|
4
|
+
const youtubeExtractId = (url) => {
|
|
5
|
+
const match = youtubeRegExp.exec(url.trim());
|
|
6
|
+
return match ? match[1] : false;
|
|
7
|
+
};
|
|
8
|
+
const videoPlayerStaticAttributes = { nocookie: true };
|
|
9
|
+
export default Node.create({
|
|
10
|
+
name: "lite-youtube",
|
|
11
|
+
content: "",
|
|
12
|
+
marks: "",
|
|
13
|
+
group: "block",
|
|
14
|
+
draggable: true,
|
|
15
|
+
addAttributes() {
|
|
16
|
+
return {
|
|
17
|
+
videoid: {
|
|
18
|
+
default: null,
|
|
19
|
+
},
|
|
20
|
+
provider: {
|
|
21
|
+
default: "youtube",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
parseHTML() {
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
tag: "lite-youtube",
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
},
|
|
32
|
+
renderHTML({ HTMLAttributes }) {
|
|
33
|
+
return [
|
|
34
|
+
"lite-youtube",
|
|
35
|
+
mergeAttributes(videoPlayerStaticAttributes, this.options.HTMLAttributes, HTMLAttributes),
|
|
36
|
+
];
|
|
37
|
+
},
|
|
38
|
+
addCommands() {
|
|
39
|
+
return {
|
|
40
|
+
insertVideoPlayer: (options) => ({ chain, editor }) => {
|
|
41
|
+
const { url } = options;
|
|
42
|
+
const videoid = youtubeExtractId(url);
|
|
43
|
+
if (videoid) {
|
|
44
|
+
const { selection } = editor.state;
|
|
45
|
+
const pos = selection.$head;
|
|
46
|
+
return chain().insertContentAt(pos.before(), [{
|
|
47
|
+
type: this.name,
|
|
48
|
+
attrs: { videoid, provider: "youtube" },
|
|
49
|
+
}]).run();
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
addProseMirrorPlugins() {
|
|
56
|
+
return [
|
|
57
|
+
new Plugin({
|
|
58
|
+
key: new PluginKey("handlePasteVideoURL"),
|
|
59
|
+
props: {
|
|
60
|
+
handlePaste: (view, _event, slice) => {
|
|
61
|
+
if (slice.content.childCount !== 1)
|
|
62
|
+
return false;
|
|
63
|
+
const { state } = view;
|
|
64
|
+
const { selection } = state;
|
|
65
|
+
const { empty } = selection;
|
|
66
|
+
if (!empty)
|
|
67
|
+
return false;
|
|
68
|
+
const pos = selection.$head;
|
|
69
|
+
const node = pos.node();
|
|
70
|
+
if (node.content.size > 0)
|
|
71
|
+
return false;
|
|
72
|
+
let textContent = "", href = "";
|
|
73
|
+
slice.content.forEach((node) => {
|
|
74
|
+
textContent += node.textContent;
|
|
75
|
+
});
|
|
76
|
+
textContent = textContent.trim();
|
|
77
|
+
let videoid = youtubeExtractId(textContent);
|
|
78
|
+
if (!videoid)
|
|
79
|
+
for (const mark of slice?.content?.content?.[0]?.marks) {
|
|
80
|
+
if (mark.attrs.href) {
|
|
81
|
+
const id = youtubeExtractId(mark.attrs.href);
|
|
82
|
+
if (id) {
|
|
83
|
+
videoid = id;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!videoid)
|
|
89
|
+
return false;
|
|
90
|
+
this.editor.chain().insertContentAt(pos.before(), [{
|
|
91
|
+
type: this.name,
|
|
92
|
+
attrs: { videoid, provider: "youtube" },
|
|
93
|
+
}]).run();
|
|
94
|
+
return true;
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
];
|
|
99
|
+
},
|
|
100
|
+
});
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
<script>import { BubbleMenu } from "svelte-tiptap";
|
|
2
|
-
import { getContext } from "svelte";
|
|
2
|
+
import { getContext, tick } from "svelte";
|
|
3
3
|
import 'tippy.js/animations/shift-away-subtle.css';
|
|
4
4
|
import ToolbarButton from "./ToolbarButton.svelte";
|
|
5
5
|
import { isTableAnySelected } from "../plugin/table/util";
|
|
6
6
|
import deleteTable from "../plugin/table/deleteTable";
|
|
7
|
-
//@ts-ignore
|
|
8
|
-
import { MathInline } from "@seorii/prosemirror-math/tiptap";
|
|
9
7
|
import setMath from "./setMath";
|
|
8
|
+
import { Button, Icon, IconButton, Input, List, OneLine, Tooltip } from "nunui";
|
|
10
9
|
const tiptap = getContext('editor');
|
|
10
|
+
let link = false, href = '', sel = '', _sel = '';
|
|
11
11
|
$: selection = $tiptap?.state?.selection;
|
|
12
12
|
$: table = isTableAnySelected(selection);
|
|
13
|
+
$: sel = selection?.from + '-' + selection?.to;
|
|
14
|
+
$: if ($tiptap && sel !== _sel) {
|
|
15
|
+
_sel = sel;
|
|
16
|
+
link = false;
|
|
17
|
+
href = $tiptap.getAttributes('link').href;
|
|
18
|
+
}
|
|
19
|
+
$: if ($tiptap) {
|
|
20
|
+
if (href)
|
|
21
|
+
$tiptap.chain().setLink({ href }).run();
|
|
22
|
+
else
|
|
23
|
+
$tiptap.chain().unsetLink().run();
|
|
24
|
+
}
|
|
13
25
|
</script>
|
|
14
26
|
|
|
15
27
|
|
|
@@ -20,7 +32,24 @@ $: table = isTableAnySelected(selection);
|
|
|
20
32
|
<slot/>
|
|
21
33
|
{:else}
|
|
22
34
|
<main>
|
|
23
|
-
{#if
|
|
35
|
+
{#if link}
|
|
36
|
+
<div class="link">
|
|
37
|
+
<p>
|
|
38
|
+
<Icon icon="link"/>
|
|
39
|
+
링크
|
|
40
|
+
</p>
|
|
41
|
+
<Input placeholder="url" fullWidth bind:value={href} autofocus/>
|
|
42
|
+
<div>
|
|
43
|
+
<Button tabindex="0" transparent small on:click={() => {
|
|
44
|
+
href = ''
|
|
45
|
+
$tiptap.chain().focus().unsetLink().run()
|
|
46
|
+
tick().then(() => link = false)
|
|
47
|
+
}}>삭제
|
|
48
|
+
</Button>
|
|
49
|
+
<Button tabindex="0" transparent small on:click={() => link = false}>닫기</Button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
{:else if table}
|
|
24
53
|
{#if table.length > 1}
|
|
25
54
|
<ToolbarButton icon="cell_merge" handler={() => $tiptap.commands.mergeCells()}/>
|
|
26
55
|
{:else}
|
|
@@ -36,12 +65,34 @@ $: table = isTableAnySelected(selection);
|
|
|
36
65
|
handler={() => $tiptap.chain().focus().addRowAfter().run()}/>
|
|
37
66
|
<ToolbarButton icon="close" handler={() => deleteTable({editor: $tiptap})}/>
|
|
38
67
|
{:else}
|
|
68
|
+
<Tooltip bottom left xstack width="160px">
|
|
69
|
+
<IconButton size="1.2em" icon="format_align_left" slot="target"/>
|
|
70
|
+
<div style="margin: -6px;font-size: 0.6em">
|
|
71
|
+
<List>
|
|
72
|
+
<OneLine icon="format_align_left" title="왼쪽 정렬"
|
|
73
|
+
on:click={() => $tiptap.chain().focus().setTextAlign('left').run()}
|
|
74
|
+
active={$tiptap.isActive({ textAlign: 'left' })}/>
|
|
75
|
+
<OneLine icon="format_align_center" title="가운데 정렬"
|
|
76
|
+
on:click={() => $tiptap.chain().focus().setTextAlign('center').run()}
|
|
77
|
+
active={$tiptap.isActive({ textAlign: 'center' })}/>
|
|
78
|
+
<OneLine icon="format_align_right" title="오른쪽 정렬"
|
|
79
|
+
on:click={() => $tiptap.chain().focus().setTextAlign('right').run()}
|
|
80
|
+
active={$tiptap.isActive({ textAlign: 'right' })}/>
|
|
81
|
+
<OneLine icon="format_align_justify" title="양쪽 정렬"
|
|
82
|
+
on:click={() => $tiptap.chain().focus().setTextAlign('justify').run()}
|
|
83
|
+
active={$tiptap.isActive({ textAlign: 'justify' })}/>
|
|
84
|
+
</List>
|
|
85
|
+
</div>
|
|
86
|
+
</Tooltip>
|
|
39
87
|
<ToolbarButton icon="format_bold" prop="bold"/>
|
|
40
88
|
<ToolbarButton icon="format_italic" prop="italic"/>
|
|
41
89
|
<ToolbarButton icon="format_strikethrough" prop="strike"/>
|
|
42
90
|
<ToolbarButton icon="format_underlined" prop="underline"/>
|
|
91
|
+
<ToolbarButton icon="superscript" prop="superscript"/>
|
|
92
|
+
<ToolbarButton icon="subscript" prop="subscript"/>
|
|
43
93
|
<ToolbarButton icon="functions" handler={() => setMath($tiptap)}/>
|
|
44
94
|
<ToolbarButton icon="code" prop="code"/>
|
|
95
|
+
<ToolbarButton icon="link" prop="link" handler={() => link = true}/>
|
|
45
96
|
{/if}
|
|
46
97
|
</main>
|
|
47
98
|
{/if}
|
|
@@ -50,7 +101,8 @@ $: table = isTableAnySelected(selection);
|
|
|
50
101
|
|
|
51
102
|
<style>main {
|
|
52
103
|
box-shadow: var(--shadow);
|
|
53
|
-
background: var(--surface);
|
|
104
|
+
background: var(--surface, #fff);
|
|
105
|
+
color: var(--on-surface, #000);
|
|
54
106
|
padding: 8px;
|
|
55
107
|
border-radius: 4px;
|
|
56
108
|
display: flex;
|
|
@@ -66,4 +118,18 @@ main > :global(*):first-child {
|
|
|
66
118
|
}
|
|
67
119
|
main > :global(*):last-child {
|
|
68
120
|
margin-right: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.link {
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
font-size: 0.7em;
|
|
127
|
+
}
|
|
128
|
+
.link p {
|
|
129
|
+
margin: 0 0 0.6em 0;
|
|
130
|
+
}
|
|
131
|
+
.link div {
|
|
132
|
+
margin-top: 0.6em;
|
|
133
|
+
display: flex;
|
|
134
|
+
justify-content: flex-end;
|
|
69
135
|
}</style>
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
<script>import { List, TwoLine } from "nunui";
|
|
1
|
+
<script>import { Button, IconButton, Input, List, TwoLine } from "nunui";
|
|
2
2
|
import { getContext } from "svelte";
|
|
3
|
-
import { slashVisible, slashItems, slashLocaltion, slashProps } from '../plugin/command/stores';
|
|
3
|
+
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail } from '../plugin/command/stores';
|
|
4
4
|
import { fly, slide } from "svelte/transition";
|
|
5
5
|
import { quartOut } from "svelte/easing";
|
|
6
6
|
const tiptap = getContext('editor');
|
|
7
7
|
export let selectedIndex = 0;
|
|
8
8
|
let height = 0, elements = [];
|
|
9
|
+
let iframe = '';
|
|
10
|
+
$: if ($slashVisible) {
|
|
11
|
+
iframe = '';
|
|
12
|
+
}
|
|
9
13
|
</script>
|
|
10
14
|
|
|
11
15
|
<svelte:window bind:innerHeight={height}/>
|
|
@@ -15,27 +19,66 @@ let height = 0, elements = [];
|
|
|
15
19
|
<main style="left: {$slashLocaltion.x}px; top: {$slashLocaltion.y + $slashLocaltion.height + 384 > height
|
|
16
20
|
? $slashLocaltion.y - $slashLocaltion.height - 384
|
|
17
21
|
: $slashLocaltion.y + $slashLocaltion.height}px;" transition:fly={{y: 10, duration: 200, easing: quartOut}}>
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
<div
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
{#if $slashDetail === 'iframe'}
|
|
23
|
+
<div class="detail">
|
|
24
|
+
<header>
|
|
25
|
+
<IconButton icon="arrow_back" on:click={() => $slashDetail = ''}/>
|
|
26
|
+
<div class="title">iframe</div>
|
|
27
|
+
</header>
|
|
28
|
+
<Input placeholder="url" fullWidth bind:value={iframe} autofocus
|
|
29
|
+
on:submit={() => $tiptap.commands.insertContent({type: 'iframe', attrs: {src: iframe}})}/>
|
|
30
|
+
<footer>
|
|
31
|
+
<Button tabindex="0" transparent small on:click={() => {
|
|
32
|
+
iframe = ''
|
|
33
|
+
$slashDetail = ''
|
|
34
|
+
}}>취소
|
|
35
|
+
</Button>
|
|
36
|
+
<Button tabindex="0" transparent small
|
|
37
|
+
on:click={() => $tiptap.commands.insertContent({type: 'iframe', attrs: {src: iframe}})}>삽입
|
|
38
|
+
</Button>
|
|
39
|
+
</footer>
|
|
40
|
+
</div>
|
|
41
|
+
{:else if $slashDetail === 'youtube'}
|
|
42
|
+
<div class="detail">
|
|
43
|
+
<header>
|
|
44
|
+
<IconButton icon="arrow_back" on:click={() => $slashDetail = ''}/>
|
|
45
|
+
<div class="title">youtube</div>
|
|
46
|
+
</header>
|
|
47
|
+
<Input placeholder="url" fullWidth bind:value={iframe} autofocus
|
|
48
|
+
on:submit={() => $tiptap.commands.insertVideoPlayer({url: iframe})}/>
|
|
49
|
+
<footer>
|
|
50
|
+
<Button tabindex="0" transparent small on:click={() => {
|
|
51
|
+
iframe = ''
|
|
52
|
+
$slashDetail = ''
|
|
53
|
+
}}>취소
|
|
54
|
+
</Button>
|
|
55
|
+
<Button tabindex="0" transparent small
|
|
56
|
+
on:click={() => $tiptap.commands.insertVideoPlayer({url: iframe})}>삽입
|
|
57
|
+
</Button>
|
|
58
|
+
</footer>
|
|
59
|
+
</div>
|
|
60
|
+
{:else}
|
|
61
|
+
<div class="list">
|
|
62
|
+
<List>
|
|
63
|
+
{#each $slashItems as {section, list}(section)}
|
|
64
|
+
<div class="section" transition:slide={{duration: 400, easing: quartOut}}>{section}</div>
|
|
65
|
+
<div transition:slide={{duration: 400, easing: quartOut}}>
|
|
66
|
+
{#each list || [] as {title, subtitle, icon, command, section}, i(title)}
|
|
67
|
+
<div transition:slide={{duration: 400, easing: quartOut}}>
|
|
68
|
+
<TwoLine on:mouseenter={() => (selectedIndex = i)} on:click={() => {
|
|
27
69
|
command?.($slashProps);
|
|
28
70
|
setTimeout(() => $tiptap.commands.focus());
|
|
29
71
|
}} bind:this={elements[i]} {icon} {title} subtitle={subtitle || ''}/>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
72
|
+
</div>
|
|
73
|
+
{/each}
|
|
74
|
+
</div>
|
|
75
|
+
{/each}
|
|
76
|
+
{#if !$slashItems.length}
|
|
77
|
+
<div class="section" transition:slide={{duration: 400, easing: quartOut}}>결과 없음</div>
|
|
78
|
+
{/if}
|
|
79
|
+
</List>
|
|
80
|
+
</div>
|
|
81
|
+
{/if}
|
|
39
82
|
</main>
|
|
40
83
|
{/if}
|
|
41
84
|
|
|
@@ -50,7 +93,7 @@ let height = 0, elements = [];
|
|
|
50
93
|
}
|
|
51
94
|
|
|
52
95
|
main {
|
|
53
|
-
position:
|
|
96
|
+
position: fixed;
|
|
54
97
|
background: var(--surface, #fff);
|
|
55
98
|
width: 220px;
|
|
56
99
|
border-radius: 4px;
|
|
@@ -79,4 +122,29 @@ main {
|
|
|
79
122
|
font-size: 0.8em !important;
|
|
80
123
|
font-weight: 300 !important;
|
|
81
124
|
color: var(--primary-dark1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.detail {
|
|
128
|
+
font-size: 0.8em;
|
|
129
|
+
padding: 8px;
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
header {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
margin-bottom: 6px;
|
|
138
|
+
}
|
|
139
|
+
header > :global(*) {
|
|
140
|
+
margin-right: 8px;
|
|
141
|
+
}
|
|
142
|
+
header > :global(*):last-child {
|
|
143
|
+
margin-right: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
footer {
|
|
147
|
+
margin-top: 0.6em;
|
|
148
|
+
display: flex;
|
|
149
|
+
justify-content: flex-end;
|
|
82
150
|
}</style>
|
|
@@ -17,10 +17,10 @@ const tiptap = getContext('editor');
|
|
|
17
17
|
<IconButton size="1.2em" icon="text_fields" slot="target"/>
|
|
18
18
|
<div style="margin: -6px">
|
|
19
19
|
<List>
|
|
20
|
-
<OneLine title="제목 1" on:click={() => $tiptap.commands.setHeading({level: 1})}/>
|
|
21
|
-
<OneLine title="제목 2" on:click={() => $tiptap.commands.setHeading({level: 2})}/>
|
|
22
|
-
<OneLine title="제목 3" on:click={() => $tiptap.commands.setHeading({level: 3})}/>
|
|
23
|
-
<OneLine title="본문" on:click={() => $tiptap.commands.setParagraph()}/>
|
|
20
|
+
<OneLine icon="counter_1" title="제목 1" on:click={() => $tiptap.commands.setHeading({level: 1})}/>
|
|
21
|
+
<OneLine icon="counter_2" title="제목 2" on:click={() => $tiptap.commands.setHeading({level: 2})}/>
|
|
22
|
+
<OneLine icon="counter_3" title="제목 3" on:click={() => $tiptap.commands.setHeading({level: 3})}/>
|
|
23
|
+
<OneLine icon="segment" title="본문" on:click={() => $tiptap.commands.setParagraph()}/>
|
|
24
24
|
</List>
|
|
25
25
|
</div>
|
|
26
26
|
</Tooltip>
|
|
@@ -8,7 +8,7 @@ import Floating from "./Floating.svelte";
|
|
|
8
8
|
import Command from "./Command.svelte";
|
|
9
9
|
import { slashItems, slashProps, slashVisible } from "../plugin/command/stores";
|
|
10
10
|
const san = (body) => sanitizeHtml(body, {
|
|
11
|
-
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'math-inline', 'math-node', 'iframe', 'tiptap-file']),
|
|
11
|
+
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'math-inline', 'math-node', 'iframe', 'tiptap-file', 'lite-youtube']),
|
|
12
12
|
allowedStyles: '*', allowedAttributes: {
|
|
13
13
|
'*': ['style', 'class'],
|
|
14
14
|
a: ['href', 'name', 'target'],
|
|
@@ -16,7 +16,8 @@ const san = (body) => sanitizeHtml(body, {
|
|
|
16
16
|
iframe: ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
|
|
17
17
|
th: ['colwidth', 'colspan', 'rowspan'],
|
|
18
18
|
td: ['colwidth', 'colspan', 'rowspan'],
|
|
19
|
-
'tiptap-file': ['id']
|
|
19
|
+
'tiptap-file': ['id'],
|
|
20
|
+
'lite-youtube': ['videoid', 'params', 'nocookie', 'title', 'provider'],
|
|
20
21
|
},
|
|
21
22
|
});
|
|
22
23
|
export let body = '', editor = false, style = '', ref = null, options = {};
|
|
@@ -28,7 +29,7 @@ if (browser) {
|
|
|
28
29
|
onMount(() => {
|
|
29
30
|
body = last = san(body);
|
|
30
31
|
mounted = true;
|
|
31
|
-
import('./tiptap').then(({ default: tt }) => {
|
|
32
|
+
Promise.all([import('./tiptap'), import("@justinribeiro/lite-youtube")]).then(([{ default: tt }]) => {
|
|
32
33
|
if (!mounted)
|
|
33
34
|
return;
|
|
34
35
|
$tiptap = tt(element, body, {
|
|
@@ -143,7 +144,7 @@ main .wrapper {
|
|
|
143
144
|
filter: drop-shadow(0 0 0.75rem var(--primary-light13));
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
.editor .iframe-wrapper.ProseMirror-selectednode {
|
|
147
|
+
.editor :global(.iframe-wrapper.ProseMirror-selectednode) {
|
|
147
148
|
outline: 3px solid var(--primary);
|
|
148
149
|
}
|
|
149
150
|
|
package/dist/tiptap/tiptap.js
CHANGED
|
@@ -22,16 +22,23 @@ import TextStyle from '@tiptap/extension-text-style';
|
|
|
22
22
|
import Iframe from "../plugin/iframe";
|
|
23
23
|
// @ts-ignore
|
|
24
24
|
import { MathInline, MathBlock } from "@seorii/prosemirror-math/tiptap";
|
|
25
|
+
import Youtube from "../plugin/youtube";
|
|
25
26
|
import command from "../plugin/command";
|
|
26
27
|
export default (element, content, { plugins = [], ...props } = {}) => new Editor({
|
|
27
28
|
element, content, ...props,
|
|
28
29
|
extensions: [
|
|
29
30
|
CodeBlockLowlight.configure({ lowlight }),
|
|
30
31
|
Image,
|
|
32
|
+
Youtube,
|
|
31
33
|
StarterKit,
|
|
32
34
|
Underline,
|
|
33
35
|
Highlight.configure({ multicolor: true }),
|
|
34
|
-
Link.configure({
|
|
36
|
+
Link.configure({
|
|
37
|
+
openOnClick: true, protocols: ['ftp', 'mailto', {
|
|
38
|
+
scheme: 'tel',
|
|
39
|
+
optionalSlashes: true
|
|
40
|
+
}]
|
|
41
|
+
}),
|
|
35
42
|
TextAlign.configure({ types: ['heading', 'paragraph', 'image'] }),
|
|
36
43
|
DropCursor,
|
|
37
44
|
orderedlist,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seorii/tiptap",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "svelte-kit sync && svelte-package",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"type": "module",
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@justinribeiro/lite-youtube": "^1.5.0",
|
|
39
40
|
"@seorii/prosemirror-math": "^0.4.2",
|
|
40
41
|
"@tiptap/core": "^2.0.4",
|
|
41
42
|
"@tiptap/extension-code": "^2.0.4",
|