@writergate/quill-image-uploader-nextjs 0.0.4 → 0.0.5
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/.babelrc +14 -14
- package/.vscode/settings.json +2 -2
- package/README.md +57 -57
- package/dist/quill.imageUploader.min.css +33 -33
- package/package.json +1 -1
- package/src/blots/image.js +29 -29
- package/src/demo.js +65 -65
- package/src/quill.imageUploader.css +33 -33
- package/src/quill.imageUploader.js +205 -173
- package/webpack.config.js +55 -55
package/.babelrc
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
{
|
2
|
-
"presets": [
|
3
|
-
[
|
4
|
-
"env",
|
5
|
-
{
|
6
|
-
"targets": {
|
7
|
-
"browsers": ["last 3 versions", "safari >= 6"]
|
8
|
-
},
|
9
|
-
"modules": false
|
10
|
-
}
|
11
|
-
]
|
12
|
-
],
|
13
|
-
"plugins": []
|
14
|
-
}
|
1
|
+
{
|
2
|
+
"presets": [
|
3
|
+
[
|
4
|
+
"env",
|
5
|
+
{
|
6
|
+
"targets": {
|
7
|
+
"browsers": ["last 3 versions", "safari >= 6"]
|
8
|
+
},
|
9
|
+
"modules": false
|
10
|
+
}
|
11
|
+
]
|
12
|
+
],
|
13
|
+
"plugins": []
|
14
|
+
}
|
package/.vscode/settings.json
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
{
|
2
|
-
"eslint.enable": false
|
1
|
+
{
|
2
|
+
"eslint.enable": false
|
3
3
|
}
|
package/README.md
CHANGED
@@ -1,58 +1,58 @@
|
|
1
|
-
# Quill ImageHandler Module for NextJS
|
2
|
-
|
3
|
-
A modifed alternative of [Quill Image Upload Plugin](https://github.com/noeloconnell/quill-image-uploader) to support NextJS
|
4
|
-
|
5
|
-
The original plugin uses css import in node_modules, which is not supported by the latest NextJS.
|
6
|
-
|
7
|
-
## Credits
|
8
|
-
The full credit for the editor plugin goes to [Quill Image Upload Plugin](https://github.com/noeloconnell/quill-image-uploader) and this is a modificaion to support the NextJS.
|
9
|
-
|
10
|
-
Therefore, keep upto date on its issue tracker whether the issue is fixed.
|
11
|
-
|
12
|
-
### Install
|
13
|
-
|
14
|
-
Install with npm:
|
15
|
-
|
16
|
-
```bash
|
17
|
-
npm install @writergate/quill-image-uploader-nextjs --save
|
18
|
-
```
|
19
|
-
|
20
|
-
### Webpack/ES6
|
21
|
-
|
22
|
-
```javascript
|
23
|
-
import Quill from "quill";
|
24
|
-
import ImageUploader from "quill.imageUploader.js";
|
25
|
-
import '@writergate/quill-image-uploader-nextjs/dist/quill.imageUploader.min.css';
|
26
|
-
|
27
|
-
Quill.register("modules/imageUploader", ImageUploader);
|
28
|
-
|
29
|
-
const quill = new Quill(editor, {
|
30
|
-
// ...
|
31
|
-
modules: {
|
32
|
-
// ...
|
33
|
-
imageUploader: {
|
34
|
-
upload: (file) => {
|
35
|
-
return new Promise((resolve, reject) => {
|
36
|
-
setTimeout(() => {
|
37
|
-
resolve(
|
38
|
-
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/JavaScript-logo.png/480px-JavaScript-logo.png"
|
39
|
-
);
|
40
|
-
}, 3500);
|
41
|
-
});
|
42
|
-
},
|
43
|
-
},
|
44
|
-
},
|
45
|
-
});
|
46
|
-
```
|
47
|
-
**Note**: It's also important that you create a quill component and dynamically import the component with SSR: false for NextJS.
|
48
|
-
|
49
|
-
e.g: No SSR Editor component includes all the above code.
|
50
|
-
|
51
|
-
```javascript
|
52
|
-
|
53
|
-
const QuillNoSSR = dynamic(
|
54
|
-
() => import('src/components/no-ssr-editor').then(md => md.default),
|
55
|
-
{ ssr: false }
|
56
|
-
);
|
57
|
-
|
1
|
+
# Quill ImageHandler Module for NextJS
|
2
|
+
|
3
|
+
A modifed alternative of [Quill Image Upload Plugin](https://github.com/noeloconnell/quill-image-uploader) to support NextJS
|
4
|
+
|
5
|
+
The original plugin uses css import in node_modules, which is not supported by the latest NextJS.
|
6
|
+
|
7
|
+
## Credits
|
8
|
+
The full credit for the editor plugin goes to [Quill Image Upload Plugin](https://github.com/noeloconnell/quill-image-uploader) and this is a modificaion to support the NextJS.
|
9
|
+
|
10
|
+
Therefore, keep upto date on its issue tracker whether the issue is fixed.
|
11
|
+
|
12
|
+
### Install
|
13
|
+
|
14
|
+
Install with npm:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
npm install @writergate/quill-image-uploader-nextjs --save
|
18
|
+
```
|
19
|
+
|
20
|
+
### Webpack/ES6
|
21
|
+
|
22
|
+
```javascript
|
23
|
+
import Quill from "quill";
|
24
|
+
import ImageUploader from "quill.imageUploader.js";
|
25
|
+
import '@writergate/quill-image-uploader-nextjs/dist/quill.imageUploader.min.css';
|
26
|
+
|
27
|
+
Quill.register("modules/imageUploader", ImageUploader);
|
28
|
+
|
29
|
+
const quill = new Quill(editor, {
|
30
|
+
// ...
|
31
|
+
modules: {
|
32
|
+
// ...
|
33
|
+
imageUploader: {
|
34
|
+
upload: (file) => {
|
35
|
+
return new Promise((resolve, reject) => {
|
36
|
+
setTimeout(() => {
|
37
|
+
resolve(
|
38
|
+
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/JavaScript-logo.png/480px-JavaScript-logo.png"
|
39
|
+
);
|
40
|
+
}, 3500);
|
41
|
+
});
|
42
|
+
},
|
43
|
+
},
|
44
|
+
},
|
45
|
+
});
|
46
|
+
```
|
47
|
+
**Note**: It's also important that you create a quill component and dynamically import the component with SSR: false for NextJS.
|
48
|
+
|
49
|
+
e.g: No SSR Editor component includes all the above code.
|
50
|
+
|
51
|
+
```javascript
|
52
|
+
|
53
|
+
const QuillNoSSR = dynamic(
|
54
|
+
() => import('src/components/no-ssr-editor').then(md => md.default),
|
55
|
+
{ ssr: false }
|
56
|
+
);
|
57
|
+
|
58
58
|
```
|
@@ -1,33 +1,33 @@
|
|
1
|
-
.image-uploading {
|
2
|
-
position: relative;
|
3
|
-
display: inline-block;
|
4
|
-
}
|
5
|
-
|
6
|
-
.image-uploading img {
|
7
|
-
max-width: 98% !important;
|
8
|
-
filter: blur(5px);
|
9
|
-
opacity: 0.3;
|
10
|
-
}
|
11
|
-
|
12
|
-
.image-uploading::before {
|
13
|
-
content: "";
|
14
|
-
box-sizing: border-box;
|
15
|
-
position: absolute;
|
16
|
-
top: 50%;
|
17
|
-
left: 50%;
|
18
|
-
width: 30px;
|
19
|
-
height: 30px;
|
20
|
-
margin-top: -15px;
|
21
|
-
margin-left: -15px;
|
22
|
-
border-radius: 50%;
|
23
|
-
border: 3px solid #ccc;
|
24
|
-
border-top-color: #1e986c;
|
25
|
-
z-index: 1;
|
26
|
-
animation: spinner 0.6s linear infinite;
|
27
|
-
}
|
28
|
-
|
29
|
-
@keyframes spinner {
|
30
|
-
to {
|
31
|
-
transform: rotate(360deg);
|
32
|
-
}
|
33
|
-
}
|
1
|
+
.image-uploading {
|
2
|
+
position: relative;
|
3
|
+
display: inline-block;
|
4
|
+
}
|
5
|
+
|
6
|
+
.image-uploading img {
|
7
|
+
max-width: 98% !important;
|
8
|
+
filter: blur(5px);
|
9
|
+
opacity: 0.3;
|
10
|
+
}
|
11
|
+
|
12
|
+
.image-uploading::before {
|
13
|
+
content: "";
|
14
|
+
box-sizing: border-box;
|
15
|
+
position: absolute;
|
16
|
+
top: 50%;
|
17
|
+
left: 50%;
|
18
|
+
width: 30px;
|
19
|
+
height: 30px;
|
20
|
+
margin-top: -15px;
|
21
|
+
margin-left: -15px;
|
22
|
+
border-radius: 50%;
|
23
|
+
border: 3px solid #ccc;
|
24
|
+
border-top-color: #1e986c;
|
25
|
+
z-index: 1;
|
26
|
+
animation: spinner 0.6s linear infinite;
|
27
|
+
}
|
28
|
+
|
29
|
+
@keyframes spinner {
|
30
|
+
to {
|
31
|
+
transform: rotate(360deg);
|
32
|
+
}
|
33
|
+
}
|
package/package.json
CHANGED
package/src/blots/image.js
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
import Quill from "quill";
|
2
|
-
|
3
|
-
const InlineBlot = Quill.import("blots/block");
|
4
|
-
|
5
|
-
class LoadingImage extends InlineBlot {
|
6
|
-
static create(src) {
|
7
|
-
const node = super.create(src);
|
8
|
-
if (src === true) return node;
|
9
|
-
|
10
|
-
const image = document.createElement("img");
|
11
|
-
image.setAttribute("src", src);
|
12
|
-
node.appendChild(image);
|
13
|
-
return node;
|
14
|
-
}
|
15
|
-
deleteAt(index, length) {
|
16
|
-
super.deleteAt(index, length);
|
17
|
-
this.cache = {};
|
18
|
-
}
|
19
|
-
static value(domNode) {
|
20
|
-
const { src, custom } = domNode.dataset;
|
21
|
-
return { src, custom };
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
LoadingImage.blotName = "imageBlot";
|
26
|
-
LoadingImage.className = "image-uploading";
|
27
|
-
LoadingImage.tagName = "span";
|
28
|
-
Quill.register({ "formats/imageBlot": LoadingImage });
|
29
|
-
|
1
|
+
import Quill from "quill";
|
2
|
+
|
3
|
+
const InlineBlot = Quill.import("blots/block");
|
4
|
+
|
5
|
+
class LoadingImage extends InlineBlot {
|
6
|
+
static create(src) {
|
7
|
+
const node = super.create(src);
|
8
|
+
if (src === true) return node;
|
9
|
+
|
10
|
+
const image = document.createElement("img");
|
11
|
+
image.setAttribute("src", src);
|
12
|
+
node.appendChild(image);
|
13
|
+
return node;
|
14
|
+
}
|
15
|
+
deleteAt(index, length) {
|
16
|
+
super.deleteAt(index, length);
|
17
|
+
this.cache = {};
|
18
|
+
}
|
19
|
+
static value(domNode) {
|
20
|
+
const { src, custom } = domNode.dataset;
|
21
|
+
return { src, custom };
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
LoadingImage.blotName = "imageBlot";
|
26
|
+
LoadingImage.className = "image-uploading";
|
27
|
+
LoadingImage.tagName = "span";
|
28
|
+
Quill.register({ "formats/imageBlot": LoadingImage });
|
29
|
+
|
30
30
|
export default LoadingImage;
|
package/src/demo.js
CHANGED
@@ -1,66 +1,66 @@
|
|
1
|
-
import Quill from "quill";
|
2
|
-
import ImageUploader from "./quill.imageUploader.js";
|
3
|
-
|
4
|
-
Quill.debug("warn");
|
5
|
-
Quill.register("modules/imageUploader", ImageUploader);
|
6
|
-
|
7
|
-
const fullToolbarOptions = [
|
8
|
-
[{ header: [1, 2, 3, false] }],
|
9
|
-
["bold", "italic"],
|
10
|
-
["clean"],
|
11
|
-
["image"],
|
12
|
-
];
|
13
|
-
var quill = new Quill("#editor", {
|
14
|
-
theme: "snow",
|
15
|
-
modules: {
|
16
|
-
toolbar: {
|
17
|
-
container: fullToolbarOptions,
|
18
|
-
},
|
19
|
-
imageUploader: {
|
20
|
-
upload: (file) => {
|
21
|
-
const fileReader = new FileReader();
|
22
|
-
return new Promise((resolve, reject) => {
|
23
|
-
fileReader.addEventListener(
|
24
|
-
"load",
|
25
|
-
() => {
|
26
|
-
let base64ImageSrc = fileReader.result;
|
27
|
-
setTimeout(() => {
|
28
|
-
resolve(base64ImageSrc);
|
29
|
-
//reject('Issue uploading file');
|
30
|
-
}, 1500);
|
31
|
-
},
|
32
|
-
false
|
33
|
-
);
|
34
|
-
|
35
|
-
if (file) {
|
36
|
-
fileReader.readAsDataURL(file);
|
37
|
-
} else {
|
38
|
-
reject("No file selected");
|
39
|
-
}
|
40
|
-
});
|
41
|
-
},
|
42
|
-
},
|
43
|
-
},
|
44
|
-
});
|
45
|
-
|
46
|
-
quill.on("text-change", function(delta, oldDelta, source) {
|
47
|
-
if (source == "api") {
|
48
|
-
console.log("An API call triggered this change.");
|
49
|
-
} else if (source == "user") {
|
50
|
-
console.log("A user action triggered this change.");
|
51
|
-
}
|
52
|
-
console.log(oldDelta, delta);
|
53
|
-
});
|
54
|
-
|
55
|
-
quill.on("selection-change", function(range, oldRange, source) {
|
56
|
-
if (range) {
|
57
|
-
if (range.length == 0) {
|
58
|
-
console.log("User cursor is on", range.index);
|
59
|
-
} else {
|
60
|
-
var text = quill.getText(range.index, range.length);
|
61
|
-
console.log("User has highlighted", text);
|
62
|
-
}
|
63
|
-
} else {
|
64
|
-
console.log("Cursor not in the editor");
|
65
|
-
}
|
1
|
+
import Quill from "quill";
|
2
|
+
import ImageUploader from "./quill.imageUploader.js";
|
3
|
+
|
4
|
+
Quill.debug("warn");
|
5
|
+
Quill.register("modules/imageUploader", ImageUploader);
|
6
|
+
|
7
|
+
const fullToolbarOptions = [
|
8
|
+
[{ header: [1, 2, 3, false] }],
|
9
|
+
["bold", "italic"],
|
10
|
+
["clean"],
|
11
|
+
["image"],
|
12
|
+
];
|
13
|
+
var quill = new Quill("#editor", {
|
14
|
+
theme: "snow",
|
15
|
+
modules: {
|
16
|
+
toolbar: {
|
17
|
+
container: fullToolbarOptions,
|
18
|
+
},
|
19
|
+
imageUploader: {
|
20
|
+
upload: (file) => {
|
21
|
+
const fileReader = new FileReader();
|
22
|
+
return new Promise((resolve, reject) => {
|
23
|
+
fileReader.addEventListener(
|
24
|
+
"load",
|
25
|
+
() => {
|
26
|
+
let base64ImageSrc = fileReader.result;
|
27
|
+
setTimeout(() => {
|
28
|
+
resolve(base64ImageSrc);
|
29
|
+
//reject('Issue uploading file');
|
30
|
+
}, 1500);
|
31
|
+
},
|
32
|
+
false
|
33
|
+
);
|
34
|
+
|
35
|
+
if (file) {
|
36
|
+
fileReader.readAsDataURL(file);
|
37
|
+
} else {
|
38
|
+
reject("No file selected");
|
39
|
+
}
|
40
|
+
});
|
41
|
+
},
|
42
|
+
},
|
43
|
+
},
|
44
|
+
});
|
45
|
+
|
46
|
+
quill.on("text-change", function(delta, oldDelta, source) {
|
47
|
+
if (source == "api") {
|
48
|
+
console.log("An API call triggered this change.");
|
49
|
+
} else if (source == "user") {
|
50
|
+
console.log("A user action triggered this change.");
|
51
|
+
}
|
52
|
+
console.log(oldDelta, delta);
|
53
|
+
});
|
54
|
+
|
55
|
+
quill.on("selection-change", function(range, oldRange, source) {
|
56
|
+
if (range) {
|
57
|
+
if (range.length == 0) {
|
58
|
+
console.log("User cursor is on", range.index);
|
59
|
+
} else {
|
60
|
+
var text = quill.getText(range.index, range.length);
|
61
|
+
console.log("User has highlighted", text);
|
62
|
+
}
|
63
|
+
} else {
|
64
|
+
console.log("Cursor not in the editor");
|
65
|
+
}
|
66
66
|
});
|
@@ -1,33 +1,33 @@
|
|
1
|
-
.image-uploading {
|
2
|
-
position: relative;
|
3
|
-
display: inline-block;
|
4
|
-
}
|
5
|
-
|
6
|
-
.image-uploading img {
|
7
|
-
max-width: 98% !important;
|
8
|
-
filter: blur(5px);
|
9
|
-
opacity: 0.3;
|
10
|
-
}
|
11
|
-
|
12
|
-
.image-uploading::before {
|
13
|
-
content: "";
|
14
|
-
box-sizing: border-box;
|
15
|
-
position: absolute;
|
16
|
-
top: 50%;
|
17
|
-
left: 50%;
|
18
|
-
width: 30px;
|
19
|
-
height: 30px;
|
20
|
-
margin-top: -15px;
|
21
|
-
margin-left: -15px;
|
22
|
-
border-radius: 50%;
|
23
|
-
border: 3px solid #ccc;
|
24
|
-
border-top-color: #1e986c;
|
25
|
-
z-index: 1;
|
26
|
-
animation: spinner 0.6s linear infinite;
|
27
|
-
}
|
28
|
-
|
29
|
-
@keyframes spinner {
|
30
|
-
to {
|
31
|
-
transform: rotate(360deg);
|
32
|
-
}
|
33
|
-
}
|
1
|
+
.image-uploading {
|
2
|
+
position: relative;
|
3
|
+
display: inline-block;
|
4
|
+
}
|
5
|
+
|
6
|
+
.image-uploading img {
|
7
|
+
max-width: 98% !important;
|
8
|
+
filter: blur(5px);
|
9
|
+
opacity: 0.3;
|
10
|
+
}
|
11
|
+
|
12
|
+
.image-uploading::before {
|
13
|
+
content: "";
|
14
|
+
box-sizing: border-box;
|
15
|
+
position: absolute;
|
16
|
+
top: 50%;
|
17
|
+
left: 50%;
|
18
|
+
width: 30px;
|
19
|
+
height: 30px;
|
20
|
+
margin-top: -15px;
|
21
|
+
margin-left: -15px;
|
22
|
+
border-radius: 50%;
|
23
|
+
border: 3px solid #ccc;
|
24
|
+
border-top-color: #1e986c;
|
25
|
+
z-index: 1;
|
26
|
+
animation: spinner 0.6s linear infinite;
|
27
|
+
}
|
28
|
+
|
29
|
+
@keyframes spinner {
|
30
|
+
to {
|
31
|
+
transform: rotate(360deg);
|
32
|
+
}
|
33
|
+
}
|
@@ -1,174 +1,206 @@
|
|
1
|
-
import LoadingImage from "./blots/image.js";
|
2
|
-
|
3
|
-
class ImageUploader {
|
4
|
-
constructor(quill, options) {
|
5
|
-
this.quill = quill;
|
6
|
-
this.options = options;
|
7
|
-
this.range = null;
|
8
|
-
|
9
|
-
if (typeof this.options.upload !== "function")
|
10
|
-
console.warn(
|
11
|
-
"[Missing config] upload function that returns a promise is required"
|
12
|
-
);
|
13
|
-
|
14
|
-
var toolbar = this.quill.getModule("toolbar");
|
15
|
-
toolbar.addHandler("image", this.selectLocalImage.bind(this));
|
16
|
-
|
17
|
-
|
18
|
-
this.
|
19
|
-
|
20
|
-
|
21
|
-
this.quill.root.addEventListener("
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
this.
|
28
|
-
this.
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
this.
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
}
|
172
|
-
|
173
|
-
|
1
|
+
import LoadingImage from "./blots/image.js";
|
2
|
+
|
3
|
+
class ImageUploader {
|
4
|
+
constructor(quill, options) {
|
5
|
+
this.quill = quill;
|
6
|
+
this.options = options;
|
7
|
+
this.range = null;
|
8
|
+
|
9
|
+
if (typeof this.options.upload !== "function")
|
10
|
+
console.warn(
|
11
|
+
"[Missing config] upload function that returns a promise is required"
|
12
|
+
);
|
13
|
+
|
14
|
+
var toolbar = this.quill.getModule("toolbar");
|
15
|
+
toolbar.addHandler("image", this.selectLocalImage.bind(this));
|
16
|
+
toolbar.addHandler("code-block", this.fixHighlighter.bind(this));
|
17
|
+
|
18
|
+
this.handleDrop = this.handleDrop.bind(this);
|
19
|
+
this.handlePaste = this.handlePaste.bind(this);
|
20
|
+
|
21
|
+
this.quill.root.addEventListener("drop", this.handleDrop, false);
|
22
|
+
this.quill.root.addEventListener("paste", this.handlePaste, false);
|
23
|
+
|
24
|
+
}
|
25
|
+
|
26
|
+
fixHighlighter() {
|
27
|
+
const range = this.quill.getSelection(true);
|
28
|
+
const formats = this.quill.getFormat(range);
|
29
|
+
// if its not a code-block yet, turn it into one.
|
30
|
+
if (!formats['code-block']) {
|
31
|
+
return quill.formatLine(range.index, 'code-block', 'user');
|
32
|
+
};
|
33
|
+
|
34
|
+
// if it was a code-block, and the user meant to remove it
|
35
|
+
// then do all the work we did up top in the keybinding function
|
36
|
+
this.quill.formatLine(range.index, 'code-block', false, 'user');
|
37
|
+
const { domNode } = this.quill.getLine(range.index)[0];
|
38
|
+
let nodes = [domNode];
|
39
|
+
|
40
|
+
while (nodes.length > 0) {
|
41
|
+
const node = nodes.shift();
|
42
|
+
// first append children to queue
|
43
|
+
nodes = nodes.concat(Array.from(node.children));
|
44
|
+
|
45
|
+
// go through class list of current node removing its "hljs" classes
|
46
|
+
for (let i = 0; i < node.classList.length; i++) {
|
47
|
+
const c = node.classList[i];
|
48
|
+
if (c.indexOf('hljs') >= 0) {
|
49
|
+
node.classList.remove(c);
|
50
|
+
i--;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
selectLocalImage() {
|
57
|
+
this.range = this.quill.getSelection();
|
58
|
+
this.fileHolder = document.createElement("input");
|
59
|
+
this.fileHolder.setAttribute("type", "file");
|
60
|
+
this.fileHolder.setAttribute("accept", "image/*");
|
61
|
+
this.fileHolder.setAttribute("style", "visibility:hidden");
|
62
|
+
|
63
|
+
this.fileHolder.onchange = this.fileChanged.bind(this);
|
64
|
+
|
65
|
+
document.body.appendChild(this.fileHolder);
|
66
|
+
|
67
|
+
this.fileHolder.click();
|
68
|
+
|
69
|
+
window.requestAnimationFrame(() => {
|
70
|
+
document.body.removeChild(this.fileHolder);
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
handleDrop(evt) {
|
75
|
+
evt.stopPropagation();
|
76
|
+
evt.preventDefault();
|
77
|
+
if (
|
78
|
+
evt.dataTransfer &&
|
79
|
+
evt.dataTransfer.files &&
|
80
|
+
evt.dataTransfer.files.length
|
81
|
+
) {
|
82
|
+
if (document.caretRangeFromPoint) {
|
83
|
+
const selection = document.getSelection();
|
84
|
+
const range = document.caretRangeFromPoint(evt.clientX, evt.clientY);
|
85
|
+
if (selection && range) {
|
86
|
+
selection.setBaseAndExtent(
|
87
|
+
range.startContainer,
|
88
|
+
range.startOffset,
|
89
|
+
range.startContainer,
|
90
|
+
range.startOffset
|
91
|
+
);
|
92
|
+
}
|
93
|
+
} else {
|
94
|
+
const selection = document.getSelection();
|
95
|
+
const range = document.caretPositionFromPoint(evt.clientX, evt.clientY);
|
96
|
+
if (selection && range) {
|
97
|
+
selection.setBaseAndExtent(
|
98
|
+
range.offsetNode,
|
99
|
+
range.offset,
|
100
|
+
range.offsetNode,
|
101
|
+
range.offset
|
102
|
+
);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
this.range = this.quill.getSelection();
|
107
|
+
let file = evt.dataTransfer.files[0];
|
108
|
+
|
109
|
+
setTimeout(() => {
|
110
|
+
this.range = this.quill.getSelection();
|
111
|
+
this.readAndUploadFile(file);
|
112
|
+
}, 0);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
handlePaste(evt) {
|
117
|
+
let clipboard = evt.clipboardData || window.clipboardData;
|
118
|
+
|
119
|
+
// IE 11 is .files other browsers are .items
|
120
|
+
if (clipboard && (clipboard.items || clipboard.files)) {
|
121
|
+
let items = clipboard.items || clipboard.files;
|
122
|
+
const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
|
123
|
+
|
124
|
+
for (let i = 0; i < items.length; i++) {
|
125
|
+
if (IMAGE_MIME_REGEX.test(items[i].type)) {
|
126
|
+
let file = items[i].getAsFile ? items[i].getAsFile() : items[i];
|
127
|
+
|
128
|
+
if (file) {
|
129
|
+
this.range = this.quill.getSelection();
|
130
|
+
evt.preventDefault();
|
131
|
+
setTimeout(() => {
|
132
|
+
this.range = this.quill.getSelection();
|
133
|
+
this.readAndUploadFile(file);
|
134
|
+
}, 0);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
readAndUploadFile(file) {
|
142
|
+
let isUploadReject = false;
|
143
|
+
|
144
|
+
const fileReader = new FileReader();
|
145
|
+
|
146
|
+
fileReader.addEventListener(
|
147
|
+
"load",
|
148
|
+
() => {
|
149
|
+
if (!isUploadReject) {
|
150
|
+
let base64ImageSrc = fileReader.result;
|
151
|
+
this.insertBase64Image(base64ImageSrc);
|
152
|
+
}
|
153
|
+
},
|
154
|
+
false
|
155
|
+
);
|
156
|
+
|
157
|
+
if (file) {
|
158
|
+
fileReader.readAsDataURL(file);
|
159
|
+
}
|
160
|
+
|
161
|
+
this.options.upload(file).then(
|
162
|
+
(imageUrl) => {
|
163
|
+
this.insertToEditor(imageUrl);
|
164
|
+
},
|
165
|
+
(error) => {
|
166
|
+
isUploadReject = true;
|
167
|
+
this.removeBase64Image();
|
168
|
+
console.warn(error);
|
169
|
+
}
|
170
|
+
);
|
171
|
+
}
|
172
|
+
|
173
|
+
fileChanged() {
|
174
|
+
const file = this.fileHolder.files[0];
|
175
|
+
this.readAndUploadFile(file);
|
176
|
+
}
|
177
|
+
|
178
|
+
insertBase64Image(url) {
|
179
|
+
const range = this.range;
|
180
|
+
this.quill.insertEmbed(
|
181
|
+
range.index,
|
182
|
+
LoadingImage.blotName,
|
183
|
+
`${url}`,
|
184
|
+
"user"
|
185
|
+
);
|
186
|
+
}
|
187
|
+
|
188
|
+
insertToEditor(url) {
|
189
|
+
const range = this.range;
|
190
|
+
// Delete the placeholder image
|
191
|
+
this.quill.deleteText(range.index, 3, "user");
|
192
|
+
// Insert the server saved image
|
193
|
+
this.quill.insertEmbed(range.index, "image", `${url}`, "user");
|
194
|
+
|
195
|
+
range.index++;
|
196
|
+
this.quill.setSelection(range, "user");
|
197
|
+
}
|
198
|
+
|
199
|
+
removeBase64Image() {
|
200
|
+
const range = this.range;
|
201
|
+
this.quill.deleteText(range.index, 3, "user");
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
window.ImageUploader = ImageUploader;
|
174
206
|
export default ImageUploader;
|
package/webpack.config.js
CHANGED
@@ -1,56 +1,56 @@
|
|
1
|
-
const path = require("path");
|
2
|
-
const TerserPlugin = require("terser-webpack-plugin");
|
3
|
-
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
4
|
-
|
5
|
-
module.exports = [{
|
6
|
-
entry: {
|
7
|
-
"quill.imageUploader": "./src/quill.imageUploader.js"
|
8
|
-
},
|
9
|
-
output: {
|
10
|
-
filename: "[name].min.js",
|
11
|
-
path: path.resolve(__dirname, "dist"),
|
12
|
-
},
|
13
|
-
devServer: {
|
14
|
-
//contentBase: './src',
|
15
|
-
https: true,
|
16
|
-
},
|
17
|
-
externals: {
|
18
|
-
quill: "Quill",
|
19
|
-
},
|
20
|
-
optimization: {
|
21
|
-
minimize: true,
|
22
|
-
minimizer: [
|
23
|
-
new TerserPlugin({
|
24
|
-
extractComments: true,
|
25
|
-
cache: true,
|
26
|
-
parallel: true,
|
27
|
-
sourceMap: true, // Must be set to true if using source-maps in production
|
28
|
-
terserOptions: {
|
29
|
-
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
|
30
|
-
extractComments: "all",
|
31
|
-
compress: {
|
32
|
-
drop_console: false,
|
33
|
-
},
|
34
|
-
},
|
35
|
-
}),
|
36
|
-
],
|
37
|
-
},
|
38
|
-
module: {
|
39
|
-
rules: [{
|
40
|
-
test: /\.css$/,
|
41
|
-
use: ExtractTextPlugin.extract({
|
42
|
-
use: [{
|
43
|
-
loader: "css-loader",
|
44
|
-
}, ],
|
45
|
-
}),
|
46
|
-
},
|
47
|
-
{
|
48
|
-
test: /\.js$/,
|
49
|
-
exclude: /node_modules/,
|
50
|
-
use: {
|
51
|
-
loader: "babel-loader",
|
52
|
-
},
|
53
|
-
},
|
54
|
-
],
|
55
|
-
}
|
1
|
+
const path = require("path");
|
2
|
+
const TerserPlugin = require("terser-webpack-plugin");
|
3
|
+
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
4
|
+
|
5
|
+
module.exports = [{
|
6
|
+
entry: {
|
7
|
+
"quill.imageUploader": "./src/quill.imageUploader.js"
|
8
|
+
},
|
9
|
+
output: {
|
10
|
+
filename: "[name].min.js",
|
11
|
+
path: path.resolve(__dirname, "dist"),
|
12
|
+
},
|
13
|
+
devServer: {
|
14
|
+
//contentBase: './src',
|
15
|
+
https: true,
|
16
|
+
},
|
17
|
+
externals: {
|
18
|
+
quill: "Quill",
|
19
|
+
},
|
20
|
+
optimization: {
|
21
|
+
minimize: true,
|
22
|
+
minimizer: [
|
23
|
+
new TerserPlugin({
|
24
|
+
extractComments: true,
|
25
|
+
cache: true,
|
26
|
+
parallel: true,
|
27
|
+
sourceMap: true, // Must be set to true if using source-maps in production
|
28
|
+
terserOptions: {
|
29
|
+
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
|
30
|
+
extractComments: "all",
|
31
|
+
compress: {
|
32
|
+
drop_console: false,
|
33
|
+
},
|
34
|
+
},
|
35
|
+
}),
|
36
|
+
],
|
37
|
+
},
|
38
|
+
module: {
|
39
|
+
rules: [{
|
40
|
+
test: /\.css$/,
|
41
|
+
use: ExtractTextPlugin.extract({
|
42
|
+
use: [{
|
43
|
+
loader: "css-loader",
|
44
|
+
}, ],
|
45
|
+
}),
|
46
|
+
},
|
47
|
+
{
|
48
|
+
test: /\.js$/,
|
49
|
+
exclude: /node_modules/,
|
50
|
+
use: {
|
51
|
+
loader: "babel-loader",
|
52
|
+
},
|
53
|
+
},
|
54
|
+
],
|
55
|
+
}
|
56
56
|
}, ];
|