@seorii/tiptap 0.1.0 → 0.1.2
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/suggest.js +22 -0
- package/dist/plugin/image/dragdrop.d.ts +4 -0
- package/dist/plugin/image/dragdrop.js +86 -0
- package/dist/plugin/{image.js → image/index.js} +5 -1
- package/dist/tiptap/Bubble.svelte +1 -1
- package/dist/tiptap/Command.svelte +4 -3
- package/dist/tiptap/TipTap.svelte +6 -4
- package/dist/tiptap/TipTap.svelte.d.ts +6 -2
- package/package.json +8 -8
- /package/dist/plugin/{image.d.ts → image/index.d.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { slashVisible, slashItems, slashLocaltion, slashProps, slashDetail } from './stores';
|
|
2
2
|
import i18n from "../../i18n";
|
|
3
|
+
import { fallbackUpload } from "../image/dragdrop";
|
|
3
4
|
export default {
|
|
4
5
|
items: ({ query }) => {
|
|
5
6
|
const raw = [
|
|
@@ -51,6 +52,27 @@ export default {
|
|
|
51
52
|
},
|
|
52
53
|
{
|
|
53
54
|
section: i18n('block'), list: [
|
|
55
|
+
{
|
|
56
|
+
icon: 'image',
|
|
57
|
+
title: i18n('image'),
|
|
58
|
+
subtitle: i18n('imageInfo'),
|
|
59
|
+
command: ({ editor, range }) => {
|
|
60
|
+
const input = document.createElement('input');
|
|
61
|
+
input.type = 'file';
|
|
62
|
+
input.accept = 'image/*';
|
|
63
|
+
input.onchange = async () => {
|
|
64
|
+
if (input.files) {
|
|
65
|
+
const file = input.files[0];
|
|
66
|
+
if (file) {
|
|
67
|
+
const upload = window.__image_uploader || fallbackUpload;
|
|
68
|
+
const src = await upload(file);
|
|
69
|
+
editor.chain().focus().deleteRange(range).setImage({ src }).run();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
input.click();
|
|
74
|
+
}
|
|
75
|
+
},
|
|
54
76
|
{
|
|
55
77
|
icon: 'code',
|
|
56
78
|
title: i18n('codeBlock'),
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Plugin, PluginKey } from 'prosemirror-state';
|
|
2
|
+
export const fallbackUpload = (async (image) => URL.createObjectURL(image));
|
|
3
|
+
export const dropImagePlugin = () => {
|
|
4
|
+
return new Plugin({
|
|
5
|
+
props: {
|
|
6
|
+
handleDOMEvents: {
|
|
7
|
+
paste(view, event) {
|
|
8
|
+
const upload = window.__image_uploader || fallbackUpload;
|
|
9
|
+
const items = Array.from(event.clipboardData?.items || []);
|
|
10
|
+
const { schema } = view.state;
|
|
11
|
+
items.forEach((item) => {
|
|
12
|
+
const image = item.getAsFile();
|
|
13
|
+
if (item.type.indexOf('image') === 0) {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
if (upload && image) {
|
|
16
|
+
upload(image).then((src) => {
|
|
17
|
+
const node = schema.nodes.image.create({
|
|
18
|
+
src: src,
|
|
19
|
+
});
|
|
20
|
+
const transaction = view.state.tr.replaceSelectionWith(node);
|
|
21
|
+
view.dispatch(transaction);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const reader = new FileReader();
|
|
27
|
+
reader.onload = (readerEvent) => {
|
|
28
|
+
const node = schema.nodes.image.create({
|
|
29
|
+
src: readerEvent.target?.result,
|
|
30
|
+
});
|
|
31
|
+
const transaction = view.state.tr.replaceSelectionWith(node);
|
|
32
|
+
view.dispatch(transaction);
|
|
33
|
+
};
|
|
34
|
+
if (!image)
|
|
35
|
+
return;
|
|
36
|
+
reader.readAsDataURL(image);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return false;
|
|
40
|
+
},
|
|
41
|
+
drop: (view, event) => {
|
|
42
|
+
const upload = window.__image_uploader || fallbackUpload;
|
|
43
|
+
const hasFiles = event.dataTransfer &&
|
|
44
|
+
event.dataTransfer.files &&
|
|
45
|
+
event.dataTransfer.files.length;
|
|
46
|
+
if (!hasFiles) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const images = Array.from(event.dataTransfer?.files ?? []).filter((file) => /image/i.test(file.type));
|
|
50
|
+
if (images.length === 0) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
const { schema } = view.state;
|
|
55
|
+
const coordinates = view.posAtCoords({
|
|
56
|
+
left: event.clientX,
|
|
57
|
+
top: event.clientY,
|
|
58
|
+
});
|
|
59
|
+
if (!coordinates)
|
|
60
|
+
return false;
|
|
61
|
+
images.forEach(async (image) => {
|
|
62
|
+
const reader = new FileReader();
|
|
63
|
+
if (upload) {
|
|
64
|
+
const node = schema.nodes.image.create({
|
|
65
|
+
src: await upload(image),
|
|
66
|
+
});
|
|
67
|
+
const transaction = view.state.tr.insert(coordinates.pos, node);
|
|
68
|
+
view.dispatch(transaction);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
reader.onload = (readerEvent) => {
|
|
72
|
+
const node = schema.nodes.image.create({
|
|
73
|
+
src: readerEvent.target?.result,
|
|
74
|
+
});
|
|
75
|
+
const transaction = view.state.tr.insert(coordinates.pos, node);
|
|
76
|
+
view.dispatch(transaction);
|
|
77
|
+
};
|
|
78
|
+
reader.readAsDataURL(image);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return true;
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Image from "@tiptap/extension-image";
|
|
2
2
|
import { mergeAttributes } from "@tiptap/core";
|
|
3
|
+
import { dropImagePlugin } from "./dragdrop";
|
|
3
4
|
export default Image.extend({
|
|
4
5
|
addOptions() {
|
|
5
6
|
return {
|
|
@@ -11,5 +12,8 @@ export default Image.extend({
|
|
|
11
12
|
renderHTML({ HTMLAttributes }) {
|
|
12
13
|
const { style } = HTMLAttributes;
|
|
13
14
|
return ["figure", { style }, ["img", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]];
|
|
14
|
-
}
|
|
15
|
+
},
|
|
16
|
+
addProseMirrorPlugins() {
|
|
17
|
+
return [dropImagePlugin()];
|
|
18
|
+
},
|
|
15
19
|
}).configure({ HTMLAttributes: { crossorigin: 'anonymous' } });
|
|
@@ -67,9 +67,9 @@ $: if ($slashVisible) {
|
|
|
67
67
|
{#each list || [] as {title, subtitle, icon, command, section}, i(title)}
|
|
68
68
|
<div transition:slide={{duration: 400, easing: quartOut}}>
|
|
69
69
|
<TwoLine on:mouseenter={() => (selectedIndex = i)} on:click={() => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
command?.($slashProps);
|
|
71
|
+
setTimeout(() => $tiptap.commands.focus());
|
|
72
|
+
}} bind:this={elements[i]} {icon} {title} subtitle={subtitle || ''}/>
|
|
73
73
|
</div>
|
|
74
74
|
{/each}
|
|
75
75
|
</div>
|
|
@@ -98,6 +98,7 @@ main {
|
|
|
98
98
|
position: fixed;
|
|
99
99
|
background: var(--surface, #fff);
|
|
100
100
|
width: 220px;
|
|
101
|
+
max-height: 384px;
|
|
101
102
|
border-radius: 4px;
|
|
102
103
|
overflow-y: scroll;
|
|
103
104
|
z-index: 10;
|
|
@@ -8,6 +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
|
import i18n from "../i18n";
|
|
11
|
+
import { fallbackUpload } from "../plugin/image/dragdrop";
|
|
11
12
|
const san = (body) => sanitizeHtml(body, {
|
|
12
13
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'math-inline', 'math-node', 'iframe', 'tiptap-file', 'lite-youtube', 'blockquote']),
|
|
13
14
|
allowedStyles: '*', allowedAttributes: {
|
|
@@ -21,11 +22,12 @@ const san = (body) => sanitizeHtml(body, {
|
|
|
21
22
|
'lite-youtube': ['videoid', 'params', 'nocookie', 'title', 'provider'],
|
|
22
23
|
},
|
|
23
24
|
});
|
|
24
|
-
export let body = '', editable = false,
|
|
25
|
+
export let body = '', editable = false, ref = null, options = {};
|
|
26
|
+
export const imageUpload = fallbackUpload, style = '';
|
|
25
27
|
const tiptap = setContext('editor', writable(null));
|
|
26
28
|
let element, fullscreen = false, mounted = false, last = '';
|
|
27
|
-
$: ref = $tiptap;
|
|
28
29
|
$: $tiptap && $tiptap.setEditable(editable);
|
|
30
|
+
$: browser && (window.__image_uploader = imageUpload);
|
|
29
31
|
if (browser) {
|
|
30
32
|
onMount(() => {
|
|
31
33
|
body = last = san(body);
|
|
@@ -33,9 +35,9 @@ if (browser) {
|
|
|
33
35
|
Promise.all([import('./tiptap'), import("@justinribeiro/lite-youtube")]).then(([{ default: tt }]) => {
|
|
34
36
|
if (!mounted)
|
|
35
37
|
return;
|
|
36
|
-
$tiptap = tt(element, body, {
|
|
38
|
+
ref = $tiptap = tt(element, body, {
|
|
37
39
|
editable: editable,
|
|
38
|
-
onTransaction: () => $tiptap = $tiptap,
|
|
40
|
+
onTransaction: () => ref = $tiptap = $tiptap,
|
|
39
41
|
...options,
|
|
40
42
|
});
|
|
41
43
|
$tiptap.on('update', ({ editor: tiptap }) => {
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
2
|
import "@seorii/prosemirror-math/style.css";
|
|
3
|
+
import type { UploadFn } from "../plugin/image/dragdrop";
|
|
3
4
|
declare const __propDef: {
|
|
4
5
|
props: {
|
|
5
6
|
body?: string | undefined;
|
|
6
7
|
editable?: boolean | undefined;
|
|
7
|
-
|
|
8
|
-
ref?: any;
|
|
8
|
+
ref?: null | undefined;
|
|
9
9
|
options?: {} | undefined;
|
|
10
|
+
imageUpload?: UploadFn | undefined;
|
|
11
|
+
style?: "" | undefined;
|
|
10
12
|
};
|
|
11
13
|
events: {
|
|
12
14
|
[evt: string]: CustomEvent<any>;
|
|
@@ -19,5 +21,7 @@ export type TipTapProps = typeof __propDef.props;
|
|
|
19
21
|
export type TipTapEvents = typeof __propDef.events;
|
|
20
22
|
export type TipTapSlots = typeof __propDef.slots;
|
|
21
23
|
export default class TipTap extends SvelteComponentTyped<TipTapProps, TipTapEvents, TipTapSlots> {
|
|
24
|
+
get imageUpload(): UploadFn;
|
|
25
|
+
get style(): "";
|
|
22
26
|
}
|
|
23
27
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seorii/tiptap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "svelte-kit sync && svelte-package",
|
|
@@ -18,21 +18,21 @@
|
|
|
18
18
|
"@sveltejs/kit": "^1.22.4",
|
|
19
19
|
"@sveltejs/package": "^2.2.0",
|
|
20
20
|
"@types/sanitize-html": "^2.9.0",
|
|
21
|
-
"@typescript-eslint/eslint-plugin": "^6.2.
|
|
22
|
-
"@typescript-eslint/parser": "^6.2.
|
|
21
|
+
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
|
22
|
+
"@typescript-eslint/parser": "^6.2.1",
|
|
23
23
|
"eslint": "^8.46.0",
|
|
24
|
-
"eslint-config-prettier": "^8.
|
|
24
|
+
"eslint-config-prettier": "^8.10.0",
|
|
25
25
|
"eslint-plugin-svelte3": "^4.0.0",
|
|
26
26
|
"gh-pages": "^5.0.0",
|
|
27
|
-
"prettier": "^3.0.
|
|
27
|
+
"prettier": "^3.0.1",
|
|
28
28
|
"prettier-plugin-svelte": "^3.0.3",
|
|
29
|
-
"sass": "^1.64.
|
|
29
|
+
"sass": "^1.64.2",
|
|
30
30
|
"svelte": "^4.1.2",
|
|
31
31
|
"svelte-check": "^3.4.6",
|
|
32
32
|
"svelte-preprocess": "^5.0.4",
|
|
33
33
|
"tslib": "^2.6.1",
|
|
34
34
|
"typescript": "^5.1.6",
|
|
35
|
-
"vite": "^4.4.
|
|
35
|
+
"vite": "^4.4.8"
|
|
36
36
|
},
|
|
37
37
|
"type": "module",
|
|
38
38
|
"dependencies": {
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@tiptap/starter-kit": "^2.0.4",
|
|
62
62
|
"@tiptap/suggestion": "^2.0.4",
|
|
63
63
|
"lowlight": "^2.9.0",
|
|
64
|
-
"nunui": "^0.0.
|
|
64
|
+
"nunui": "^0.0.101",
|
|
65
65
|
"prosemirror-commands": "^1.5.2",
|
|
66
66
|
"prosemirror-model": "^1.19.3",
|
|
67
67
|
"prosemirror-state": "^1.4.3",
|
|
File without changes
|