@writergate/quill-image-uploader-nextjs 0.1.8 → 0.1.9

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.
@@ -1 +1 @@
1
- !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=Quill},function(e,t,n){"use strict";n.r(t);var i=n(0),r=n.n(i),o=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),a=function e(t,n,i){null===t&&(t=Function.prototype);var r=Object.getOwnPropertyDescriptor(t,n);if(void 0===r){var o=Object.getPrototypeOf(t);return null===o?void 0:e(o,n,i)}if("value"in r)return r.value;var a=r.get;return void 0!==a?a.call(i):void 0};function l(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var u=function(e){function t(){return l(this,t),s(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),o(t,[{key:"deleteAt",value:function(e,n){a(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"deleteAt",this).call(this,e,n),this.cache={}}}],[{key:"create",value:function(e){var n=a(t.__proto__||Object.getPrototypeOf(t),"create",this).call(this,e);if(!0===e)return n;var i=document.createElement("img");return i.setAttribute("src",e),n.appendChild(i),n}},{key:"value",value:function(e){var t=e.dataset;return{src:t.src,custom:t.custom}}}]),t}(r.a.import("blots/block"));u.blotName="imageBlot",u.className="image-uploading",u.tagName="span",r.a.register({"formats/imageBlot":u});var c=u,f=(n(3),function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}());var d=function(){function e(t,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.quill=t,this.options=n,this.range=null,"function"!=typeof this.options.upload&&console.warn("[Missing config] upload function that returns a promise is required"),this.quill.getModule("toolbar").addHandler("image",this.selectLocalImage.bind(this)),this.handleDrop=this.handleDrop.bind(this),this.handlePaste=this.handlePaste.bind(this),this.quill.root.addEventListener("drop",this.handleDrop,!1),this.quill.root.addEventListener("paste",this.handlePaste,!1)}return f(e,[{key:"selectLocalImage",value:function(){var e=this;this.range=this.quill.getSelection(),this.fileHolder=document.createElement("input"),this.fileHolder.setAttribute("type","file"),this.fileHolder.setAttribute("accept","image/*"),this.fileHolder.setAttribute("style","visibility:hidden"),this.fileHolder.onchange=this.fileChanged.bind(this),document.body.appendChild(this.fileHolder),this.fileHolder.click(),window.requestAnimationFrame((function(){document.body.removeChild(e.fileHolder)}))}},{key:"handleDrop",value:function(e){var t=this;if(e.stopPropagation(),e.preventDefault(),e.dataTransfer&&e.dataTransfer.files&&e.dataTransfer.files.length){if(document.caretRangeFromPoint){var n=document.getSelection(),i=document.caretRangeFromPoint(e.clientX,e.clientY);n&&i&&n.setBaseAndExtent(i.startContainer,i.startOffset,i.startContainer,i.startOffset)}else{var r=document.getSelection(),o=document.caretPositionFromPoint(e.clientX,e.clientY);r&&o&&r.setBaseAndExtent(o.offsetNode,o.offset,o.offsetNode,o.offset)}this.range=this.quill.getSelection();var a=e.dataTransfer.files[0];setTimeout((function(){t.range=t.quill.getSelection(),t.readAndUploadFile(a)}),0)}}},{key:"handlePaste",value:function(e){var t=this,n=e.clipboardData||window.clipboardData;if(n&&(n.items||n.files))for(var i=n.items||n.files,r=/^image\/(jpe?g|gif|png|svg|webp)$/i,o=0;o<i.length;o++)r.test(i[o].type)&&function(){var n=i[o].getAsFile?i[o].getAsFile():i[o];n&&(t.range=t.quill.getSelection(),e.preventDefault(),setTimeout((function(){t.range=t.quill.getSelection(),t.readAndUploadFile(n)}),0))}()}},{key:"readAndUploadFile",value:function(e){var t=this,n=!1,i=new FileReader;i.addEventListener("load",(function(){if(!n){var e=i.result;t.insertBase64Image(e)}}),!1),e&&i.readAsDataURL(e),this.options.upload(e).then((function(e){t.insertToEditor(e)}),(function(e){n=!0,t.removeBase64Image(),console.warn(e)}))}},{key:"fileChanged",value:function(){var e=this.fileHolder.files[0];this.readAndUploadFile(e)}},{key:"insertBase64Image",value:function(e){var t=this.range;this.quill.insertEmbed(t.index,c.blotName,""+e,"user")}},{key:"insertToEditor",value:function(e){var t=this.range;this.quill.deleteText(t.index,3,"user"),this.quill.insertEmbed(t.index,"image",""+e,"user"),t.index++,this.quill.setSelection(t,"user")}},{key:"removeBase64Image",value:function(){var e=this.range;this.quill.deleteText(e.index,3,"user")}}]),e}();window.ImageUploader=d;t.default=d},,function(e,t){}]);
1
+ !function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=Quill},function(e,t,n){"use strict";n.r(t);var i=n(0),o=n.n(i),r=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),l=function e(t,n,i){null===t&&(t=Function.prototype);var o=Object.getOwnPropertyDescriptor(t,n);if(void 0===o){var r=Object.getPrototypeOf(t);return null===r?void 0:e(r,n,i)}if("value"in o)return o.value;var l=o.get;return void 0!==l?l.call(i):void 0};function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var u=function(e){function t(){return a(this,t),s(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"deleteAt",value:function(e,n){l(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"deleteAt",this).call(this,e,n),this.cache={}}}],[{key:"create",value:function(e){var n=l(t.__proto__||Object.getPrototypeOf(t),"create",this).call(this,e);if(!0===e)return n;var i=document.createElement("img");return i.setAttribute("src",e),n.appendChild(i),n}},{key:"value",value:function(e){var t=e.dataset;return{src:t.src,custom:t.custom}}}]),t}(o.a.import("blots/block"));u.blotName="imageBlot",u.className="image-uploading",u.tagName="span",o.a.register({"formats/imageBlot":u});var c=u,f=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();function d(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function h(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var p=function(e){function t(){return d(this,t),h(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),f(t,null,[{key:"create",value:function(e){var n=function e(t,n,i){null===t&&(t=Function.prototype);var o=Object.getOwnPropertyDescriptor(t,n);if(void 0===o){var r=Object.getPrototypeOf(t);return null===r?void 0:e(r,n,i)}if("value"in o)return o.value;var l=o.get;return void 0!==l?l.call(i):void 0}(t.__proto__||Object.getPrototypeOf(t),"create",this).call(this);return console.log(e),n.setAttribute("data-comment",e),n}},{key:"formats",value:function(e){return e.getAttribute("data-comment")||!0}}]),t}(o.a.import("blots/block"));p.blotName="hiddenCommentBlot",p.className="hidden-comment",p.tagName="span",o.a.register({"formats/hiddenCommentBlot":p});var m=p,g=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();var v=function(){function e(t,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.quill=t,this.options=n,this.range=null,"function"!=typeof this.options.upload&&console.warn("[Missing config] upload function that returns a promise is required"),"function"!=typeof this.options.newComment&&console.warn("[Missing config] newComment function that returns a promise is required"),"function"!=typeof this.options.showComments&&console.warn("[Missing config] showComments function that returns a promise is required");var i=this.quill.getModule("toolbar");i.addHandler("image",this.selectLocalImage.bind(this)),i.addHandler("code-block",this.fixHighlighter.bind(this)),i.addHandler("clean",this.clean.bind(this)),i.addHandler("underline",this.addComment.bind(this)),i.addHandler("link",this.toggleLink.bind(this)),this.handleDrop=this.handleDrop.bind(this),this.handlePaste=this.handlePaste.bind(this),this.quill.root.addEventListener("drop",this.handleDrop,!1),this.quill.root.addEventListener("paste",this.handlePaste,!1)}return g(e,[{key:"toggleLink",value:function(e){e?this.quill.theme.tooltip.edit():this.quill.theme.tooltip.edit("link",this.quill.getFormat().link)}},{key:"addComment",value:function(){var e=this.quill.getSelection();console.log(e);var t=this.quill.getText(e.index,e.length);e&&e.length>0&&this.options.newComment&&(this.quill.insertEmbed(e.index-1,m.blotName,"new-comment","silent"),e.top=this.quill.getBounds(e.index,e.length).top,this.options.newComment(e,t),this.quill.insertEmbed(e.index,m.blotName,"new-comment-1","silent")),this.quill.theme.tooltip.hide()}},{key:"fixHighlighter",value:function(){var e=this.quill.getSelection(!0);if(!this.quill.getFormat(e)["code-block"])return this.quill.formatLine(e.index,e.length,"code-block","user");this.quill.removeFormat(e.index,e.length,"user"),this.quill.removeFormat(e.index,e.length,"user")}},{key:"clean",value:function(){var e=this.quill.getSelection(!0);this.quill.getFormat(e);this.quill.removeFormat(e.index,e.length,"user"),this.quill.removeFormat(e.index,e.length,"user")}},{key:"selectLocalImage",value:function(){var e=this;this.range=this.quill.getSelection(),this.fileHolder=document.createElement("input"),this.fileHolder.setAttribute("type","file"),this.fileHolder.setAttribute("accept","image/*"),this.fileHolder.setAttribute("style","visibility:hidden"),this.fileHolder.onchange=this.fileChanged.bind(this),document.body.appendChild(this.fileHolder),this.fileHolder.click(),window.requestAnimationFrame((function(){document.body.removeChild(e.fileHolder)}))}},{key:"handleDrop",value:function(e){var t=this;if(e.stopPropagation(),e.preventDefault(),e.dataTransfer&&e.dataTransfer.files&&e.dataTransfer.files.length){if(document.caretRangeFromPoint){var n=document.getSelection(),i=document.caretRangeFromPoint(e.clientX,e.clientY);n&&i&&n.setBaseAndExtent(i.startContainer,i.startOffset,i.startContainer,i.startOffset)}else{var o=document.getSelection(),r=document.caretPositionFromPoint(e.clientX,e.clientY);o&&r&&o.setBaseAndExtent(r.offsetNode,r.offset,r.offsetNode,r.offset)}this.range=this.quill.getSelection();var l=e.dataTransfer.files[0];setTimeout((function(){t.range=t.quill.getSelection(),t.readAndUploadFile(l)}),0)}}},{key:"handlePaste",value:function(e){var t=this,n=e.clipboardData||window.clipboardData;if(n&&(n.items||n.files))for(var i=n.items||n.files,o=/^image\/(jpe?g|gif|png|svg|webp)$/i,r=0;r<i.length;r++)o.test(i[r].type)&&function(){var n=i[r].getAsFile?i[r].getAsFile():i[r];n&&(t.range=t.quill.getSelection(),e.preventDefault(),setTimeout((function(){t.range=t.quill.getSelection(),t.readAndUploadFile(n)}),0))}()}},{key:"readAndUploadFile",value:function(e){var t=this,n=!1,i=new FileReader;i.addEventListener("load",(function(){if(!n){var e=i.result;t.insertBase64Image(e)}}),!1),e&&i.readAsDataURL(e),this.options.upload(e).then((function(e){t.insertToEditor(e)}),(function(e){n=!0,t.removeBase64Image(),console.warn(e)}))}},{key:"fileChanged",value:function(){var e=this.fileHolder.files[0];this.readAndUploadFile(e)}},{key:"insertBase64Image",value:function(e){var t=this.range;this.quill.insertEmbed(t.index,c.blotName,""+e,"user")}},{key:"insertToEditor",value:function(e){var t=this.range;this.quill.deleteText(t.index,3,"user"),this.quill.insertEmbed(t.index,"image",""+e,"user"),t.index++,this.quill.setSelection(t,"user")}},{key:"removeBase64Image",value:function(){var e=this.range;this.quill.deleteText(e.index,3,"user")}}]),e}();window.ImageUploader=v;t.default=v}]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writergate/quill-image-uploader-nextjs",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/enlear/quill-image-uploader-nextjs.git"
@@ -58,5 +58,9 @@
58
58
  "bugs": {
59
59
  "url": "https://github.com/enlear/quill-image-uploader-nextjs/issues"
60
60
  },
61
- "homepage": "https://github.com/enlear/quill-image-uploader-nextjs#readme"
62
- }
61
+ "homepage": "https://github.com/enlear/quill-image-uploader-nextjs#readme",
62
+ "dependencies": {
63
+ "nanoid": "^4.0.2",
64
+ "quill-delta-to-html": "^0.12.1"
65
+ }
66
+ }
@@ -0,0 +1,28 @@
1
+ import Quill from "quill";
2
+
3
+ const InlineBlot = Quill.import("blots/block");
4
+
5
+ class CommentBlot extends InlineBlot {
6
+ /**
7
+ *
8
+ * @param {{commentId:string, commentInfo:string}} Comment ID of creating comment + DOM representation of selected content
9
+ * @returns
10
+ */
11
+ static create({ commentId, commentInfo }) {
12
+ const node = super.create();
13
+ node.setAttribute("data-comment", commentId);
14
+ node.innerHtml = commentInfo;
15
+ return node;
16
+ }
17
+
18
+ static formats(node) {
19
+ return node.getAttribute("data-comment") || true;
20
+ }
21
+ }
22
+
23
+ CommentBlot.blotName = "CommentBlot";
24
+ CommentBlot.className = "comment";
25
+ CommentBlot.tagName = "span";
26
+ Quill.register({ "formats/hiddenCommentBlot": CommentBlot });
27
+
28
+ export default CommentBlot;
package/src/demo.js CHANGED
@@ -5,62 +5,62 @@ Quill.debug("warn");
5
5
  Quill.register("modules/imageUploader", ImageUploader);
6
6
 
7
7
  const fullToolbarOptions = [
8
- [{ header: [1, 2, 3, false] }],
9
- ["bold", "italic"],
10
- ["clean"],
11
- ["image"],
8
+ [{ header: [1, 2, 3, false] }],
9
+ ["bold", "italic"],
10
+ ["clean"],
11
+ ["image"],
12
12
  ];
13
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
- });
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);
41
31
  },
42
- },
32
+ false
33
+ );
34
+
35
+ if (file) {
36
+ fileReader.readAsDataURL(file);
37
+ } else {
38
+ reject("No file selected");
39
+ }
40
+ });
41
+ },
43
42
  },
43
+ },
44
44
  });
45
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);
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
53
  });
54
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
- }
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);
63
59
  } else {
64
- console.log("Cursor not in the editor");
60
+ var text = quill.getText(range.index, range.length);
61
+ console.log("User has highlighted", text);
65
62
  }
66
- });
63
+ } else {
64
+ console.log("Cursor not in the editor");
65
+ }
66
+ });
@@ -1,229 +1,254 @@
1
1
  import LoadingImage from "./blots/image.js";
2
+ import CommentBlot from "./blots/comment.js";
3
+ import GenerateRandomId from "./utils/nano-id.js";
4
+ import DeltaToHtml from "./utils/delta-to-html.js";
2
5
 
3
6
  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
- if (typeof this.options.newComment !== "function")
15
- console.warn(
16
- "[Missing config] newComment function that returns a promise is required"
17
- );
18
-
19
- if (typeof this.options.showComments !== "function")
20
- console.warn(
21
- "[Missing config] showComments function that returns a promise is required"
22
- );
23
-
24
- var toolbar = this.quill.getModule("toolbar");
25
- toolbar.addHandler("image", this.selectLocalImage.bind(this));
26
- toolbar.addHandler("code-block", this.fixHighlighter.bind(this));
27
- toolbar.addHandler("clean", this.clean.bind(this));
28
- toolbar.addHandler("underline", this.addComment.bind(this));
29
- toolbar.addHandler('link', this.toggleLink.bind(this));
30
-
31
- this.handleDrop = this.handleDrop.bind(this);
32
- this.handlePaste = this.handlePaste.bind(this);
33
-
34
- this.quill.root.addEventListener("drop", this.handleDrop, false);
35
- this.quill.root.addEventListener("paste", this.handlePaste, false);
7
+ constructor(quill, options) {
8
+ this.quill = quill;
9
+ this.options = options;
10
+ this.range = null;
11
+
12
+ if (typeof this.options.upload !== "function")
13
+ console.warn(
14
+ "[Missing config] upload function that returns a promise is required"
15
+ );
16
+
17
+ if (typeof this.options.newComment !== "function")
18
+ console.warn(
19
+ "[Missing config] newComment function that returns a promise is required"
20
+ );
21
+
22
+ if (typeof this.options.showComments !== "function")
23
+ console.warn(
24
+ "[Missing config] showComments function that returns a promise is required"
25
+ );
26
+
27
+ var toolbar = this.quill.getModule("toolbar");
28
+ toolbar.addHandler("image", this.selectLocalImage.bind(this));
29
+ toolbar.addHandler("code-block", this.fixHighlighter.bind(this));
30
+ toolbar.addHandler("clean", this.clean.bind(this));
31
+ toolbar.addHandler("underline", this.addComment.bind(this));
32
+ toolbar.addHandler("link", this.toggleLink.bind(this));
33
+
34
+ this.handleDrop = this.handleDrop.bind(this);
35
+ this.handlePaste = this.handlePaste.bind(this);
36
+
37
+ this.quill.root.addEventListener("drop", this.handleDrop, false);
38
+ this.quill.root.addEventListener("paste", this.handlePaste, false);
39
+ }
40
+
41
+ toggleLink(formatTextToLink) {
42
+ if (formatTextToLink) {
43
+ this.quill.theme.tooltip.edit();
44
+ } else {
45
+ this.quill.theme.tooltip.edit("link", this.quill.getFormat().link);
36
46
  }
37
-
38
- toggleLink(formatTextToLink) {
39
- if (formatTextToLink) {
40
- this.quill.theme.tooltip.edit();
41
- } else {
42
- this.quill.theme.tooltip.edit('link', this.quill.getFormat().link);
43
- }
47
+ }
48
+
49
+ addComment() {
50
+ const commentId = GenerateRandomId();
51
+ var range = this.quill.getSelection();
52
+ if (range && range.length > 0 && this.options.newComment) {
53
+ range.top = this.quill.getBounds(range.index, range.length).top;
54
+
55
+ const selectedContents = this.quill.getContents(
56
+ range.index,
57
+ range.length
58
+ );
59
+
60
+ const selectedText = this.quill.getText(range.index, range.length);
61
+ const selectedHTML = DeltaToHtml(selectedContents);
62
+
63
+ console.log({ selectedContents, selectedText, selectedHTML });
64
+
65
+ this.quill.deleteText(range.index, range.length);
66
+ this.quill.insertEmbed(
67
+ range.index,
68
+ CommentBlot.blotName,
69
+ { commentId, commentInfo: selectedHTML },
70
+ "silent"
71
+ );
72
+ this.options.newComment(range, selectedText, commentId);
44
73
  }
45
-
46
- addComment() {
47
- var range = this.quill.getSelection();
48
- var selectedText = this.quill.getText(range.index, range.length);
49
-
50
- if (range && range.length > 0 && this.options.newComment) {
51
- range.top = this.quill.getBounds(range.index, range.length).top
52
- this.options.newComment(range, selectedText);
53
- }
54
- this.quill.theme.tooltip.hide();
74
+ this.quill.theme.tooltip.hide();
75
+ }
76
+
77
+ fixHighlighter() {
78
+ const range = this.quill.getSelection(true);
79
+ const formats = this.quill.getFormat(range);
80
+ // if its not a code-block yet, turn it into one.
81
+ if (!formats["code-block"]) {
82
+ return this.quill.formatLine(
83
+ range.index,
84
+ range.length,
85
+ "code-block",
86
+ "user"
87
+ );
55
88
  }
56
89
 
57
- fixHighlighter() {
58
- const range = this.quill.getSelection(true);
59
- const formats = this.quill.getFormat(range);
60
- // if its not a code-block yet, turn it into one.
61
- if (!formats['code-block']) {
62
- return this.quill.formatLine(range.index, range.length, 'code-block', 'user');
63
- };
64
-
65
- // if it was a code-block, and the user meant to remove it
66
- this.quill.removeFormat(range.index, range.length, 'user');
67
- // running it twise to remove colors
68
- this.quill.removeFormat(range.index, range.length, 'user');
69
- }
90
+ // if it was a code-block, and the user meant to remove it
91
+ this.quill.removeFormat(range.index, range.length, "user");
92
+ // running it twise to remove colors
93
+ this.quill.removeFormat(range.index, range.length, "user");
94
+ }
95
+
96
+ clean() {
97
+ const range = this.quill.getSelection(true);
98
+ const formats = this.quill.getFormat(range);
99
+ // running it twise to remove colors
100
+ this.quill.removeFormat(range.index, range.length, "user");
101
+ this.quill.removeFormat(range.index, range.length, "user");
102
+ }
103
+
104
+ selectLocalImage() {
105
+ this.range = this.quill.getSelection();
106
+ this.fileHolder = document.createElement("input");
107
+ this.fileHolder.setAttribute("type", "file");
108
+ this.fileHolder.setAttribute("accept", "image/*");
109
+ this.fileHolder.setAttribute("style", "visibility:hidden");
110
+
111
+ this.fileHolder.onchange = this.fileChanged.bind(this);
112
+
113
+ document.body.appendChild(this.fileHolder);
114
+
115
+ this.fileHolder.click();
116
+
117
+ window.requestAnimationFrame(() => {
118
+ document.body.removeChild(this.fileHolder);
119
+ });
120
+ }
121
+
122
+ handleDrop(evt) {
123
+ evt.stopPropagation();
124
+ evt.preventDefault();
125
+ if (
126
+ evt.dataTransfer &&
127
+ evt.dataTransfer.files &&
128
+ evt.dataTransfer.files.length
129
+ ) {
130
+ if (document.caretRangeFromPoint) {
131
+ const selection = document.getSelection();
132
+ const range = document.caretRangeFromPoint(evt.clientX, evt.clientY);
133
+ if (selection && range) {
134
+ selection.setBaseAndExtent(
135
+ range.startContainer,
136
+ range.startOffset,
137
+ range.startContainer,
138
+ range.startOffset
139
+ );
140
+ }
141
+ } else {
142
+ const selection = document.getSelection();
143
+ const range = document.caretPositionFromPoint(evt.clientX, evt.clientY);
144
+ if (selection && range) {
145
+ selection.setBaseAndExtent(
146
+ range.offsetNode,
147
+ range.offset,
148
+ range.offsetNode,
149
+ range.offset
150
+ );
151
+ }
152
+ }
70
153
 
71
- clean() {
72
- const range = this.quill.getSelection(true);
73
- const formats = this.quill.getFormat(range);
74
- // running it twise to remove colors
75
- this.quill.removeFormat(range.index, range.length, 'user');
76
- this.quill.removeFormat(range.index, range.length, 'user');
77
- }
154
+ this.range = this.quill.getSelection();
155
+ let file = evt.dataTransfer.files[0];
78
156
 
79
- selectLocalImage() {
157
+ setTimeout(() => {
80
158
  this.range = this.quill.getSelection();
81
- this.fileHolder = document.createElement("input");
82
- this.fileHolder.setAttribute("type", "file");
83
- this.fileHolder.setAttribute("accept", "image/*");
84
- this.fileHolder.setAttribute("style", "visibility:hidden");
85
-
86
- this.fileHolder.onchange = this.fileChanged.bind(this);
87
-
88
- document.body.appendChild(this.fileHolder);
159
+ this.readAndUploadFile(file);
160
+ }, 0);
161
+ }
162
+ }
89
163
 
90
- this.fileHolder.click();
164
+ handlePaste(evt) {
165
+ let clipboard = evt.clipboardData || window.clipboardData;
91
166
 
92
- window.requestAnimationFrame(() => {
93
- document.body.removeChild(this.fileHolder);
94
- });
95
- }
167
+ // IE 11 is .files other browsers are .items
168
+ if (clipboard && (clipboard.items || clipboard.files)) {
169
+ let items = clipboard.items || clipboard.files;
170
+ const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
96
171
 
97
- handleDrop(evt) {
98
- evt.stopPropagation();
99
- evt.preventDefault();
100
- if (
101
- evt.dataTransfer &&
102
- evt.dataTransfer.files &&
103
- evt.dataTransfer.files.length
104
- ) {
105
- if (document.caretRangeFromPoint) {
106
- const selection = document.getSelection();
107
- const range = document.caretRangeFromPoint(evt.clientX, evt.clientY);
108
- if (selection && range) {
109
- selection.setBaseAndExtent(
110
- range.startContainer,
111
- range.startOffset,
112
- range.startContainer,
113
- range.startOffset
114
- );
115
- }
116
- } else {
117
- const selection = document.getSelection();
118
- const range = document.caretPositionFromPoint(evt.clientX, evt.clientY);
119
- if (selection && range) {
120
- selection.setBaseAndExtent(
121
- range.offsetNode,
122
- range.offset,
123
- range.offsetNode,
124
- range.offset
125
- );
126
- }
127
- }
172
+ for (let i = 0; i < items.length; i++) {
173
+ if (IMAGE_MIME_REGEX.test(items[i].type)) {
174
+ let file = items[i].getAsFile ? items[i].getAsFile() : items[i];
128
175
 
176
+ if (file) {
129
177
  this.range = this.quill.getSelection();
130
- let file = evt.dataTransfer.files[0];
131
-
178
+ evt.preventDefault();
132
179
  setTimeout(() => {
133
- this.range = this.quill.getSelection();
134
- this.readAndUploadFile(file);
180
+ this.range = this.quill.getSelection();
181
+ this.readAndUploadFile(file);
135
182
  }, 0);
183
+ }
136
184
  }
185
+ }
137
186
  }
187
+ }
138
188
 
139
- handlePaste(evt) {
140
- let clipboard = evt.clipboardData || window.clipboardData;
141
-
142
- // IE 11 is .files other browsers are .items
143
- if (clipboard && (clipboard.items || clipboard.files)) {
144
- let items = clipboard.items || clipboard.files;
145
- const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
146
-
147
- for (let i = 0; i < items.length; i++) {
148
- if (IMAGE_MIME_REGEX.test(items[i].type)) {
149
- let file = items[i].getAsFile ? items[i].getAsFile() : items[i];
150
-
151
- if (file) {
152
- this.range = this.quill.getSelection();
153
- evt.preventDefault();
154
- setTimeout(() => {
155
- this.range = this.quill.getSelection();
156
- this.readAndUploadFile(file);
157
- }, 0);
158
- }
159
- }
160
- }
161
- }
162
- }
163
-
164
- readAndUploadFile(file) {
165
- let isUploadReject = false;
189
+ readAndUploadFile(file) {
190
+ let isUploadReject = false;
166
191
 
167
- const fileReader = new FileReader();
192
+ const fileReader = new FileReader();
168
193
 
169
- fileReader.addEventListener(
170
- "load",
171
- () => {
172
- if (!isUploadReject) {
173
- let base64ImageSrc = fileReader.result;
174
- this.insertBase64Image(base64ImageSrc);
175
- }
176
- },
177
- false
178
- );
179
-
180
- if (file) {
181
- fileReader.readAsDataURL(file);
194
+ fileReader.addEventListener(
195
+ "load",
196
+ () => {
197
+ if (!isUploadReject) {
198
+ let base64ImageSrc = fileReader.result;
199
+ this.insertBase64Image(base64ImageSrc);
182
200
  }
201
+ },
202
+ false
203
+ );
183
204
 
184
- this.options.upload(file).then(
185
- (imageUrl) => {
186
- this.insertToEditor(imageUrl);
187
- },
188
- (error) => {
189
- isUploadReject = true;
190
- this.removeBase64Image();
191
- console.warn(error);
192
- }
193
- );
194
- }
195
-
196
- fileChanged() {
197
- const file = this.fileHolder.files[0];
198
- this.readAndUploadFile(file);
199
- }
200
-
201
- insertBase64Image(url) {
202
- const range = this.range;
203
- this.quill.insertEmbed(
204
- range.index,
205
- LoadingImage.blotName,
206
- `${url}`,
207
- "user"
208
- );
205
+ if (file) {
206
+ fileReader.readAsDataURL(file);
209
207
  }
210
208
 
211
- insertToEditor(url) {
212
- const range = this.range;
213
- // Delete the placeholder image
214
- this.quill.deleteText(range.index, 3, "user");
215
- // Insert the server saved image
216
- this.quill.insertEmbed(range.index, "image", `${url}`, "user");
217
-
218
- range.index++;
219
- this.quill.setSelection(range, "user");
220
- }
221
-
222
- removeBase64Image() {
223
- const range = this.range;
224
- this.quill.deleteText(range.index, 3, "user");
225
- }
209
+ this.options.upload(file).then(
210
+ (imageUrl) => {
211
+ this.insertToEditor(imageUrl);
212
+ },
213
+ (error) => {
214
+ isUploadReject = true;
215
+ this.removeBase64Image();
216
+ console.warn(error);
217
+ }
218
+ );
219
+ }
220
+
221
+ fileChanged() {
222
+ const file = this.fileHolder.files[0];
223
+ this.readAndUploadFile(file);
224
+ }
225
+
226
+ insertBase64Image(url) {
227
+ const range = this.range;
228
+ this.quill.insertEmbed(
229
+ range.index,
230
+ LoadingImage.blotName,
231
+ `${url}`,
232
+ "user"
233
+ );
234
+ }
235
+
236
+ insertToEditor(url) {
237
+ const range = this.range;
238
+ // Delete the placeholder image
239
+ this.quill.deleteText(range.index, 3, "user");
240
+ // Insert the server saved image
241
+ this.quill.insertEmbed(range.index, "image", `${url}`, "user");
242
+
243
+ range.index++;
244
+ this.quill.setSelection(range, "user");
245
+ }
246
+
247
+ removeBase64Image() {
248
+ const range = this.range;
249
+ this.quill.deleteText(range.index, 3, "user");
250
+ }
226
251
  }
227
252
 
228
253
  window.ImageUploader = ImageUploader;
229
- export default ImageUploader;
254
+ export default ImageUploader;
@@ -0,0 +1,15 @@
1
+ const QuillDeltaToHtmlConverter =
2
+ require("quill-delta-to-html").QuillDeltaToHtmlConverter;
3
+
4
+ /**
5
+ *
6
+ * @param deltaOpsn The Delta Operation to convert to HTML
7
+ * @param cfg The configuration of the Converter - https://github.com/nozer/quill-delta-to-html#configuration
8
+ * @description https://github.com/nozer/quill-delta-to-html
9
+ */
10
+ const deltaToHtml = (deltaOpsn, cfg = {}) => {
11
+ const converter = new QuillDeltaToHtmlConverter(deltaOps, cfg);
12
+ return converter.convert();
13
+ };
14
+
15
+ export default deltaToHtml;
@@ -0,0 +1,9 @@
1
+ const nanoid = require("nanoid");
2
+ const alphabet = nanoid.customAlphabet(
3
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
4
+ 9
5
+ );
6
+
7
+ const generateRandomId = () => alphabet();
8
+
9
+ export default generateRandomId;