@writergate/quill-image-uploader-nextjs 0.1.7 → 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.7",
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,220 +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
-
30
- this.handleDrop = this.handleDrop.bind(this);
31
- this.handlePaste = this.handlePaste.bind(this);
32
-
33
- this.quill.root.addEventListener("drop", this.handleDrop, false);
34
- 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);
35
46
  }
36
-
37
- addComment() {
38
- var range = this.quill.getSelection();
39
- var selectedText = this.quill.getText(range.index, range.length);
40
-
41
- if (range && range.length > 0 && this.options.newComment) {
42
- range.top = this.quill.getBounds(range.index, range.length).top
43
- this.options.newComment(range, selectedText);
44
- }
45
- this.quill.theme.tooltip.hide();
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);
46
73
  }
47
-
48
- fixHighlighter() {
49
- const range = this.quill.getSelection(true);
50
- const formats = this.quill.getFormat(range);
51
- // if its not a code-block yet, turn it into one.
52
- if (!formats['code-block']) {
53
- return this.quill.formatLine(range.index, range.length, 'code-block', 'user');
54
- };
55
-
56
- // if it was a code-block, and the user meant to remove it
57
- this.quill.removeFormat(range.index, range.length, 'user');
58
- // running it twise to remove colors
59
- this.quill.removeFormat(range.index, range.length, 'user');
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
+ );
60
88
  }
61
89
 
62
- clean() {
63
- const range = this.quill.getSelection(true);
64
- const formats = this.quill.getFormat(range);
65
- // running it twise to remove colors
66
- this.quill.removeFormat(range.index, range.length, 'user');
67
- this.quill.removeFormat(range.index, range.length, 'user');
68
- }
69
-
70
- selectLocalImage() {
71
- this.range = this.quill.getSelection();
72
- this.fileHolder = document.createElement("input");
73
- this.fileHolder.setAttribute("type", "file");
74
- this.fileHolder.setAttribute("accept", "image/*");
75
- this.fileHolder.setAttribute("style", "visibility:hidden");
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
+ }
76
153
 
77
- this.fileHolder.onchange = this.fileChanged.bind(this);
154
+ this.range = this.quill.getSelection();
155
+ let file = evt.dataTransfer.files[0];
78
156
 
79
- document.body.appendChild(this.fileHolder);
157
+ setTimeout(() => {
158
+ this.range = this.quill.getSelection();
159
+ this.readAndUploadFile(file);
160
+ }, 0);
161
+ }
162
+ }
80
163
 
81
- this.fileHolder.click();
164
+ handlePaste(evt) {
165
+ let clipboard = evt.clipboardData || window.clipboardData;
82
166
 
83
- window.requestAnimationFrame(() => {
84
- document.body.removeChild(this.fileHolder);
85
- });
86
- }
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;
87
171
 
88
- handleDrop(evt) {
89
- evt.stopPropagation();
90
- evt.preventDefault();
91
- if (
92
- evt.dataTransfer &&
93
- evt.dataTransfer.files &&
94
- evt.dataTransfer.files.length
95
- ) {
96
- if (document.caretRangeFromPoint) {
97
- const selection = document.getSelection();
98
- const range = document.caretRangeFromPoint(evt.clientX, evt.clientY);
99
- if (selection && range) {
100
- selection.setBaseAndExtent(
101
- range.startContainer,
102
- range.startOffset,
103
- range.startContainer,
104
- range.startOffset
105
- );
106
- }
107
- } else {
108
- const selection = document.getSelection();
109
- const range = document.caretPositionFromPoint(evt.clientX, evt.clientY);
110
- if (selection && range) {
111
- selection.setBaseAndExtent(
112
- range.offsetNode,
113
- range.offset,
114
- range.offsetNode,
115
- range.offset
116
- );
117
- }
118
- }
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];
119
175
 
176
+ if (file) {
120
177
  this.range = this.quill.getSelection();
121
- let file = evt.dataTransfer.files[0];
122
-
178
+ evt.preventDefault();
123
179
  setTimeout(() => {
124
- this.range = this.quill.getSelection();
125
- this.readAndUploadFile(file);
180
+ this.range = this.quill.getSelection();
181
+ this.readAndUploadFile(file);
126
182
  }, 0);
183
+ }
127
184
  }
185
+ }
128
186
  }
187
+ }
129
188
 
130
- handlePaste(evt) {
131
- let clipboard = evt.clipboardData || window.clipboardData;
132
-
133
- // IE 11 is .files other browsers are .items
134
- if (clipboard && (clipboard.items || clipboard.files)) {
135
- let items = clipboard.items || clipboard.files;
136
- const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
137
-
138
- for (let i = 0; i < items.length; i++) {
139
- if (IMAGE_MIME_REGEX.test(items[i].type)) {
140
- let file = items[i].getAsFile ? items[i].getAsFile() : items[i];
141
-
142
- if (file) {
143
- this.range = this.quill.getSelection();
144
- evt.preventDefault();
145
- setTimeout(() => {
146
- this.range = this.quill.getSelection();
147
- this.readAndUploadFile(file);
148
- }, 0);
149
- }
150
- }
151
- }
152
- }
153
- }
154
-
155
- readAndUploadFile(file) {
156
- let isUploadReject = false;
157
-
158
- const fileReader = new FileReader();
189
+ readAndUploadFile(file) {
190
+ let isUploadReject = false;
159
191
 
160
- fileReader.addEventListener(
161
- "load",
162
- () => {
163
- if (!isUploadReject) {
164
- let base64ImageSrc = fileReader.result;
165
- this.insertBase64Image(base64ImageSrc);
166
- }
167
- },
168
- false
169
- );
192
+ const fileReader = new FileReader();
170
193
 
171
- if (file) {
172
- fileReader.readAsDataURL(file);
194
+ fileReader.addEventListener(
195
+ "load",
196
+ () => {
197
+ if (!isUploadReject) {
198
+ let base64ImageSrc = fileReader.result;
199
+ this.insertBase64Image(base64ImageSrc);
173
200
  }
201
+ },
202
+ false
203
+ );
174
204
 
175
- this.options.upload(file).then(
176
- (imageUrl) => {
177
- this.insertToEditor(imageUrl);
178
- },
179
- (error) => {
180
- isUploadReject = true;
181
- this.removeBase64Image();
182
- console.warn(error);
183
- }
184
- );
185
- }
186
-
187
- fileChanged() {
188
- const file = this.fileHolder.files[0];
189
- this.readAndUploadFile(file);
205
+ if (file) {
206
+ fileReader.readAsDataURL(file);
190
207
  }
191
208
 
192
- insertBase64Image(url) {
193
- const range = this.range;
194
- this.quill.insertEmbed(
195
- range.index,
196
- LoadingImage.blotName,
197
- `${url}`,
198
- "user"
199
- );
200
- }
201
-
202
- insertToEditor(url) {
203
- const range = this.range;
204
- // Delete the placeholder image
205
- this.quill.deleteText(range.index, 3, "user");
206
- // Insert the server saved image
207
- this.quill.insertEmbed(range.index, "image", `${url}`, "user");
208
-
209
- range.index++;
210
- this.quill.setSelection(range, "user");
211
- }
212
-
213
- removeBase64Image() {
214
- const range = this.range;
215
- this.quill.deleteText(range.index, 3, "user");
216
- }
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
+ }
217
251
  }
218
252
 
219
253
  window.ImageUploader = ImageUploader;
220
- 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;