@bigbinary/neeto-editor 0.2.0 → 0.2.1
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/index.js +27 -6
- package/package.json +7 -4
- package/src/Common/Button.js +95 -0
- package/src/Common/Description.js +1 -5
- package/src/Common/Dropdown/index.js +6 -2
- package/src/Common/Input.js +70 -0
- package/src/Common/Label.js +45 -0
- package/src/Common/Modal.js +91 -0
- package/src/Common/Tab.js +79 -0
- package/src/Common/ToolTip.js +37 -0
- package/src/Editor/CustomExtensions/BubbleMenu/index.js +2 -2
- package/src/Editor/CustomExtensions/FixedMenu/FontSizeOption.js +3 -3
- package/src/Editor/CustomExtensions/FixedMenu/TextColorOption.js +1 -1
- package/src/Editor/CustomExtensions/FixedMenu/index.js +7 -10
- package/src/Editor/CustomExtensions/Image/LinkUploader/URLForm.js +39 -0
- package/src/Editor/CustomExtensions/Image/LocalUploader.js +21 -0
- package/src/Editor/CustomExtensions/Image/ProgressBar.js +34 -0
- package/src/Editor/CustomExtensions/Image/Uploader.js +72 -31
- package/src/Editor/CustomExtensions/Image/constants.js +5 -0
- package/src/Editor/CustomExtensions/Mention/ExtensionConfig.js +0 -1
- package/src/Editor/CustomExtensions/Mention/MentionList.js +1 -1
- package/src/Editor/CustomExtensions/SlashCommands/ExtensionConfig.js +180 -176
- package/src/Editor/CustomExtensions/Variable/index.js +3 -3
- package/src/Editor/CustomExtensions/useCustomExtensions.js +2 -1
- package/src/Editor/index.js +17 -5
- package/src/constants/regexp.js +1 -0
- package/src/examples/constants.js +1 -1
- package/src/examples/index.js +25 -25
- package/src/hooks/useTabBar.js +9 -0
- package/src/index.scss +5 -0
- package/src/styles/abstracts/_neeto-ui-variables.scss +80 -9
- package/src/styles/abstracts/_variables.scss +4 -1
- package/src/styles/components/_button.scss +161 -0
- package/src/styles/components/_editor.scss +4 -0
- package/src/styles/components/_fixed-menu.scss +4 -0
- package/src/styles/components/_image-uploader.scss +109 -0
- package/src/styles/components/_input.scss +165 -0
- package/src/styles/components/_tab.scss +74 -0
- package/src/styles/components/_tooltip.scss +152 -0
- package/webpack.config.js +7 -0
- package/webpack.dev.config.js +7 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
import { Close } from "@bigbinary/neeto-icons";
|
|
4
|
+
|
|
5
|
+
const ProgressBar = ({ uppy }) => {
|
|
6
|
+
const [progress, setProgress] = useState(0);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
uppy.on("progress", setProgress);
|
|
10
|
+
}, [uppy]);
|
|
11
|
+
|
|
12
|
+
const progressPercentage = `${progress}%`;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="progress-bar__root">
|
|
16
|
+
<div className="flex items-center justify-between">
|
|
17
|
+
<span className="progress-bar__percent-text">{progressPercentage}</span>
|
|
18
|
+
<button onClick={uppy.cancelAll}>
|
|
19
|
+
<Close size={16} />
|
|
20
|
+
</button>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="progress-bar__indicator">
|
|
23
|
+
<div className="flex h-full">
|
|
24
|
+
<div
|
|
25
|
+
style={{ width: progressPercentage }}
|
|
26
|
+
className="flex flex-col justify-center text-center text-white shadow-none whitespace-nowrap progress-bar__indicator-inner"
|
|
27
|
+
></div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default ProgressBar;
|
|
@@ -1,40 +1,81 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState, useMemo } from "react";
|
|
2
2
|
import { view } from "@risingstack/react-easy-state";
|
|
3
3
|
import Uppy from "@uppy/core";
|
|
4
|
-
import { DashboardModal } from "@uppy/react";
|
|
5
4
|
import XHRUpload from "@uppy/xhr-upload";
|
|
6
|
-
import "
|
|
7
|
-
import "
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
5
|
+
import Modal from "common/Modal";
|
|
6
|
+
import Tab from "common/Tab";
|
|
7
|
+
import useTabBar from "hooks/useTabBar";
|
|
8
|
+
|
|
9
|
+
import LocalUploader from "./LocalUploader";
|
|
10
|
+
import ProgressBar from "./ProgressBar";
|
|
11
|
+
|
|
12
|
+
import URLForm from "./LinkUploader/URLForm";
|
|
13
|
+
|
|
14
|
+
import { IMAGE_UPLOAD_OPTIONS } from "./constants";
|
|
15
|
+
|
|
16
|
+
const ImageUpload = ({ editor, imageUploadUrl, isVisible, setIsVisible }) => {
|
|
17
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
18
|
+
const [activeTab, setActiveTab] = useTabBar(IMAGE_UPLOAD_OPTIONS);
|
|
19
|
+
|
|
20
|
+
const uppy = useMemo(
|
|
21
|
+
() =>
|
|
22
|
+
new Uppy({
|
|
23
|
+
allowMultipleUploads: false,
|
|
24
|
+
autoProceed: true,
|
|
25
|
+
debug: true,
|
|
26
|
+
})
|
|
27
|
+
.use(XHRUpload, {
|
|
28
|
+
endpoint: imageUploadUrl || "/api/v1/direct_uploads",
|
|
29
|
+
formData: true,
|
|
30
|
+
fieldName: "blob",
|
|
31
|
+
})
|
|
32
|
+
.on("upload", () => setIsUploading(true))
|
|
33
|
+
.on("upload-success", (file, response) => {
|
|
34
|
+
const url = response.body.imageURL;
|
|
35
|
+
editor.chain().focus().setImage({ src: url }).run();
|
|
36
|
+
setIsVisible(false);
|
|
37
|
+
})
|
|
38
|
+
.on("cancel-all", () => setIsUploading(false))
|
|
39
|
+
.on("complete", () => setIsUploading(false)),
|
|
40
|
+
[editor]
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const handleUrlFormSubmit = (url) => {
|
|
25
44
|
editor.chain().focus().setImage({ src: url }).run();
|
|
26
|
-
|
|
45
|
+
setIsVisible(false);
|
|
46
|
+
};
|
|
27
47
|
|
|
28
48
|
return (
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
<Modal isVisible={isVisible} onClose={() => setIsVisible(false)}>
|
|
50
|
+
<div className="image-uploader__root">
|
|
51
|
+
<Tab>
|
|
52
|
+
{IMAGE_UPLOAD_OPTIONS.map((option) => (
|
|
53
|
+
<Tab.Item
|
|
54
|
+
active={activeTab === option.key}
|
|
55
|
+
onClick={() => setActiveTab(option)}
|
|
56
|
+
>
|
|
57
|
+
{option.title}
|
|
58
|
+
</Tab.Item>
|
|
59
|
+
))}
|
|
60
|
+
</Tab>
|
|
61
|
+
|
|
62
|
+
<div className="image-uploader__content">
|
|
63
|
+
{isUploading ? (
|
|
64
|
+
<div className="flex flex-col items-center justify-center flex-1 text-center">
|
|
65
|
+
<span className="label--primary">Uploading...</span>
|
|
66
|
+
<span className="label--secondary">
|
|
67
|
+
{uppy.getFiles()[0]?.name}
|
|
68
|
+
</span>
|
|
69
|
+
<ProgressBar uppy={uppy} />
|
|
70
|
+
</div>
|
|
71
|
+
) : activeTab === "local" ? (
|
|
72
|
+
<LocalUploader uppy={uppy} />
|
|
73
|
+
) : activeTab === "link" ? (
|
|
74
|
+
<URLForm onSubmit={handleUrlFormSubmit} />
|
|
75
|
+
) : null}
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</Modal>
|
|
38
79
|
);
|
|
39
80
|
};
|
|
40
81
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import sharedState from "../../sharedState";
|
|
2
1
|
import { Extension } from "@tiptap/core";
|
|
3
2
|
import CommandsList from "./CommandsList";
|
|
4
3
|
import { PluginKey } from "prosemirror-state";
|
|
@@ -19,187 +18,192 @@ import {
|
|
|
19
18
|
|
|
20
19
|
export const CommandsPluginKey = new PluginKey("commands");
|
|
21
20
|
|
|
22
|
-
export default
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
items: () => {
|
|
37
|
-
return [
|
|
38
|
-
{
|
|
39
|
-
title: "Paragraph",
|
|
40
|
-
description: "Add a plain text block",
|
|
41
|
-
Icon: Paragraph,
|
|
42
|
-
command: ({ editor, range }) => {
|
|
43
|
-
editor
|
|
44
|
-
.chain()
|
|
45
|
-
.focus()
|
|
46
|
-
.deleteRange(range)
|
|
47
|
-
.setNode("paragraph")
|
|
48
|
-
.run();
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
title: "H1",
|
|
53
|
-
description: "Add a big heading",
|
|
54
|
-
Icon: TextH1,
|
|
55
|
-
command: ({ editor, range }) => {
|
|
56
|
-
editor
|
|
57
|
-
.chain()
|
|
58
|
-
.focus()
|
|
59
|
-
.deleteRange(range)
|
|
60
|
-
.setNode("heading", { level: 1 })
|
|
61
|
-
.run();
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
title: "H2",
|
|
66
|
-
description: "Add a sub-heading",
|
|
67
|
-
Icon: TextH2,
|
|
68
|
-
command: ({ editor, range }) => {
|
|
69
|
-
editor
|
|
70
|
-
.chain()
|
|
71
|
-
.focus()
|
|
72
|
-
.deleteRange(range)
|
|
73
|
-
.setNode("heading", { level: 2 })
|
|
74
|
-
.run();
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
title: "Numbered list",
|
|
79
|
-
description: "Add a list with numbering",
|
|
80
|
-
Icon: ListNumber,
|
|
81
|
-
command: ({ editor, range }) => {
|
|
82
|
-
editor
|
|
83
|
-
.chain()
|
|
84
|
-
.focus()
|
|
85
|
-
.deleteRange(range)
|
|
86
|
-
.toggleOrderedList()
|
|
87
|
-
.run();
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
title: "Bulleted list",
|
|
92
|
-
description: "Add an list bullets",
|
|
93
|
-
Icon: ListDot,
|
|
94
|
-
command: ({ editor, range }) => {
|
|
95
|
-
editor
|
|
96
|
-
.chain()
|
|
97
|
-
.focus()
|
|
98
|
-
.deleteRange(range)
|
|
99
|
-
.toggleBulletList()
|
|
100
|
-
.run();
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
title: "Blockquote",
|
|
105
|
-
description: "Add a quote",
|
|
106
|
-
Icon: Blockquote,
|
|
107
|
-
command: ({ editor, range }) => {
|
|
108
|
-
editor
|
|
109
|
-
.chain()
|
|
110
|
-
.focus()
|
|
111
|
-
.deleteRange(range)
|
|
112
|
-
.toggleBlockquote()
|
|
113
|
-
.run();
|
|
114
|
-
},
|
|
21
|
+
export default {
|
|
22
|
+
configure: ({ setImageUploadVisible }) =>
|
|
23
|
+
Extension.create({
|
|
24
|
+
addOptions() {
|
|
25
|
+
return {
|
|
26
|
+
HTMLAttributes: {
|
|
27
|
+
class: "commands",
|
|
28
|
+
},
|
|
29
|
+
suggestion: {
|
|
30
|
+
char: "/",
|
|
31
|
+
startOfLine: false,
|
|
32
|
+
pluginKey: CommandsPluginKey,
|
|
33
|
+
command: ({ editor, range, props }) => {
|
|
34
|
+
props.command({ editor, range });
|
|
115
35
|
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
36
|
+
|
|
37
|
+
items: () => {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
title: "Paragraph",
|
|
41
|
+
description: "Add a plain text block",
|
|
42
|
+
Icon: Paragraph,
|
|
43
|
+
command: ({ editor, range }) => {
|
|
44
|
+
editor
|
|
45
|
+
.chain()
|
|
46
|
+
.focus()
|
|
47
|
+
.deleteRange(range)
|
|
48
|
+
.setNode("paragraph")
|
|
49
|
+
.run();
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "H1",
|
|
54
|
+
description: "Add a big heading",
|
|
55
|
+
Icon: TextH1,
|
|
56
|
+
command: ({ editor, range }) => {
|
|
57
|
+
editor
|
|
58
|
+
.chain()
|
|
59
|
+
.focus()
|
|
60
|
+
.deleteRange(range)
|
|
61
|
+
.setNode("heading", { level: 1 })
|
|
62
|
+
.run();
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: "H2",
|
|
67
|
+
description: "Add a sub-heading",
|
|
68
|
+
Icon: TextH2,
|
|
69
|
+
command: ({ editor, range }) => {
|
|
70
|
+
editor
|
|
71
|
+
.chain()
|
|
72
|
+
.focus()
|
|
73
|
+
.deleteRange(range)
|
|
74
|
+
.setNode("heading", { level: 2 })
|
|
75
|
+
.run();
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: "Numbered list",
|
|
80
|
+
description: "Add a list with numbering",
|
|
81
|
+
Icon: ListNumber,
|
|
82
|
+
command: ({ editor, range }) => {
|
|
83
|
+
editor
|
|
84
|
+
.chain()
|
|
85
|
+
.focus()
|
|
86
|
+
.deleteRange(range)
|
|
87
|
+
.toggleOrderedList()
|
|
88
|
+
.run();
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: "Bulleted list",
|
|
93
|
+
description: "Add an list bullets",
|
|
94
|
+
Icon: ListDot,
|
|
95
|
+
command: ({ editor, range }) => {
|
|
96
|
+
editor
|
|
97
|
+
.chain()
|
|
98
|
+
.focus()
|
|
99
|
+
.deleteRange(range)
|
|
100
|
+
.toggleBulletList()
|
|
101
|
+
.run();
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: "Blockquote",
|
|
106
|
+
description: "Add a quote",
|
|
107
|
+
Icon: Blockquote,
|
|
108
|
+
command: ({ editor, range }) => {
|
|
109
|
+
editor
|
|
110
|
+
.chain()
|
|
111
|
+
.focus()
|
|
112
|
+
.deleteRange(range)
|
|
113
|
+
.toggleBlockquote()
|
|
114
|
+
.run();
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
title: "Image",
|
|
119
|
+
description: "Add an image",
|
|
120
|
+
Icon: Image,
|
|
121
|
+
command: ({ editor, range }) => {
|
|
122
|
+
setImageUploadVisible(true);
|
|
123
|
+
editor.chain().focus().deleteRange(range).run();
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
title: "Youtube/Vimeo",
|
|
128
|
+
description: "Embed a video from major services",
|
|
129
|
+
Icon: Video,
|
|
130
|
+
command: ({ editor, range }) => {
|
|
131
|
+
const embedURL = prompt(
|
|
132
|
+
"Please enter Youtube/Vimeo embed URL"
|
|
133
|
+
);
|
|
134
|
+
editor
|
|
135
|
+
.chain()
|
|
136
|
+
.focus()
|
|
137
|
+
.deleteRange(range)
|
|
138
|
+
.setExternalVideo({ src: embedURL })
|
|
139
|
+
.run();
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
title: "Code block",
|
|
144
|
+
description: "Add a code block with syntax highlighting",
|
|
145
|
+
Icon: Code,
|
|
146
|
+
command: ({ editor, range }) => {
|
|
147
|
+
editor
|
|
148
|
+
.chain()
|
|
149
|
+
.focus()
|
|
150
|
+
.deleteRange(range)
|
|
151
|
+
.toggleCodeBlock()
|
|
152
|
+
.run();
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
];
|
|
152
156
|
},
|
|
153
|
-
];
|
|
154
|
-
},
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
render: () => {
|
|
159
|
+
let reactRenderer;
|
|
160
|
+
let popup;
|
|
159
161
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
return {
|
|
163
|
+
onStart: (props) => {
|
|
164
|
+
reactRenderer = new ReactRenderer(CommandsList, {
|
|
165
|
+
props,
|
|
166
|
+
editor: props.editor,
|
|
167
|
+
});
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
169
|
+
popup = tippy("body", {
|
|
170
|
+
getReferenceClientRect: props.clientRect,
|
|
171
|
+
appendTo: () => document.body,
|
|
172
|
+
content: reactRenderer.element,
|
|
173
|
+
showOnCreate: true,
|
|
174
|
+
interactive: true,
|
|
175
|
+
trigger: "manual",
|
|
176
|
+
placement: "bottom-start",
|
|
177
|
+
arrow: false,
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
onUpdate(props) {
|
|
181
|
+
reactRenderer.updateProps(props);
|
|
179
182
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
popup[0].setProps({
|
|
184
|
+
getReferenceClientRect: props.clientRect,
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
onKeyDown(props) {
|
|
188
|
+
return reactRenderer.ref?.onKeyDown(props);
|
|
189
|
+
},
|
|
190
|
+
onExit() {
|
|
191
|
+
popup[0].destroy();
|
|
192
|
+
reactRenderer.destroy();
|
|
193
|
+
},
|
|
194
|
+
};
|
|
183
195
|
},
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
},
|
|
187
|
-
onExit() {
|
|
188
|
-
popup[0].destroy();
|
|
189
|
-
reactRenderer.destroy();
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
},
|
|
196
|
+
},
|
|
197
|
+
};
|
|
193
198
|
},
|
|
194
|
-
};
|
|
195
|
-
},
|
|
196
199
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
})
|
|
200
|
+
addProseMirrorPlugins() {
|
|
201
|
+
return [
|
|
202
|
+
Suggestion({
|
|
203
|
+
editor: this.editor,
|
|
204
|
+
...this.options.suggestion,
|
|
205
|
+
}),
|
|
206
|
+
];
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import VariableList from "./VariableList";
|
|
4
|
-
import Dropdown from "
|
|
5
|
-
import { HashtagFilled } from "
|
|
4
|
+
import Dropdown from "common/Dropdown";
|
|
5
|
+
import { HashtagFilled } from "common/Icons";
|
|
6
6
|
|
|
7
7
|
import { MENU_ICON_SIZE } from "../FixedMenu/constants";
|
|
8
8
|
|
|
@@ -20,7 +20,7 @@ const Variables = ({ editor, variables }) => {
|
|
|
20
20
|
return (
|
|
21
21
|
<Dropdown
|
|
22
22
|
customTarget={() => (
|
|
23
|
-
<button className="relative p-3 editor-fixed-menu--item variable-selection-popup">
|
|
23
|
+
<button className="relative h-full p-3 editor-fixed-menu--item variable-selection-popup">
|
|
24
24
|
<HashtagFilled size={MENU_ICON_SIZE} />
|
|
25
25
|
</button>
|
|
26
26
|
)}
|
|
@@ -27,6 +27,7 @@ export default function useCustomExtensions({
|
|
|
27
27
|
variables,
|
|
28
28
|
isSlashCommandsActive,
|
|
29
29
|
showImageInMention,
|
|
30
|
+
setImageUploadVisible,
|
|
30
31
|
}) {
|
|
31
32
|
let customExtensions;
|
|
32
33
|
|
|
@@ -62,7 +63,7 @@ export default function useCustomExtensions({
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
if (isSlashCommandsActive) {
|
|
65
|
-
customExtensions.push(SlashCommands);
|
|
66
|
+
customExtensions.push(SlashCommands.configure({ setImageUploadVisible }));
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
if (!isEmpty(mentions)) {
|
package/src/Editor/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import classNames from "classnames";
|
|
3
3
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
4
4
|
|
|
@@ -34,6 +34,8 @@ const Tiptap = (
|
|
|
34
34
|
},
|
|
35
35
|
ref
|
|
36
36
|
) => {
|
|
37
|
+
const [isImageUploadVisible, setImageUploadVisible] = useState(false);
|
|
38
|
+
|
|
37
39
|
const isFixedMenuActive = menuType === "fixed";
|
|
38
40
|
const isBubbleMenuActive = menuType === "bubble";
|
|
39
41
|
const isSlashCommandsActive = !hideSlashCommands;
|
|
@@ -47,6 +49,7 @@ const Tiptap = (
|
|
|
47
49
|
variables,
|
|
48
50
|
isSlashCommandsActive,
|
|
49
51
|
showImageInMention,
|
|
52
|
+
setImageUploadVisible,
|
|
50
53
|
});
|
|
51
54
|
|
|
52
55
|
const editor = useEditor({
|
|
@@ -76,16 +79,25 @@ const Tiptap = (
|
|
|
76
79
|
}));
|
|
77
80
|
|
|
78
81
|
return (
|
|
79
|
-
|
|
82
|
+
<div>
|
|
80
83
|
{isFixedMenuActive ? (
|
|
81
|
-
<FixedMenu
|
|
84
|
+
<FixedMenu
|
|
85
|
+
editor={editor}
|
|
86
|
+
variables={variables}
|
|
87
|
+
setImageUploadVisible={setImageUploadVisible}
|
|
88
|
+
/>
|
|
82
89
|
) : null}
|
|
83
90
|
{isBubbleMenuActive ? (
|
|
84
91
|
<BubbleMenu editor={editor} formatterOptions={formatterOptions} />
|
|
85
92
|
) : null}
|
|
86
|
-
<ImageUploader
|
|
93
|
+
<ImageUploader
|
|
94
|
+
isVisible={isImageUploadVisible}
|
|
95
|
+
setIsVisible={setImageUploadVisible}
|
|
96
|
+
editor={editor}
|
|
97
|
+
imageUploadUrl={uploadEndpoint}
|
|
98
|
+
/>
|
|
87
99
|
<EditorContent editor={editor} {...otherProps} />
|
|
88
|
-
|
|
100
|
+
</div>
|
|
89
101
|
);
|
|
90
102
|
};
|
|
91
103
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const UrlRegExp = /(http)?s?:?(\/\/[^"']*)/;
|
|
@@ -89,7 +89,7 @@ export const STRINGS = {
|
|
|
89
89
|
<Editor placeholder="Input text here" />`,
|
|
90
90
|
|
|
91
91
|
forceTitleSampleCode: `
|
|
92
|
-
const placeholder = {
|
|
92
|
+
const placeholder = { heading: 'Input title here' };
|
|
93
93
|
|
|
94
94
|
<Editor placeholder={placeholder} forceTitle />`,
|
|
95
95
|
};
|