├── .babelrc ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── README.md ├── bower.json ├── demo ├── .gitkeep ├── index.html └── screenshot.png ├── dist ├── index.html ├── quill-inline-comment.css └── quill-inline-comment.js ├── package-lock.json ├── package.json ├── src ├── module-inline-comment.js ├── quill-inline-comment.js └── scss │ ├── base.scss │ └── core │ ├── _styles.scss │ └── _variables.scss └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["es2015"] } 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | 4 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Building 4 | 5 | To build __quill-inline-comment__, run the following command: 6 | 7 | ```npm run build``` 8 | 9 | ## Testing 10 | 11 | Still need to add testing 12 | 13 | ## Releases 14 | 15 | Still need to come up with release schemes 16 | 17 | ## Maintainers 18 | 19 | __@yeminhtut__ 20 | __@himynameistimli__ 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quill Inline Comment 2 | Module extension for [Quill.js](https://github.com/quilljs/quill) that handles inline comment like medium. 3 | 4 | #### This module is still in active development 5 | 6 | ## Usage 7 | ### Webpack/ES6 8 | 9 | ```javascript 10 | const toolbarOptions = { 11 | container: [ 12 | ['bold', 'italic', 'underline', 'strike'], 13 | ['comment'], 14 | ], 15 | handlers: {'comment': function() {}} 16 | } 17 | const quill = new Quill(editor, { 18 | // ... 19 | modules: { 20 | // ... 21 | toolbar: toolbarOptions, 22 | inline_comment: true 23 | } 24 | }); 25 | ``` 26 | 27 | ## Contributing 28 | 29 | Please check out our [contributing guidelines](CONTRIBUTING.md). 30 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quill-inline-comment", 3 | "version": "0.0.1", 4 | "description": "Quill Extension for Inline Comment Like Medium", 5 | "main": "webpack.config.js", 6 | "authors": [ 7 | "contentco" 8 | ], 9 | "license": "MIT", 10 | "keywords": [ 11 | "content", 12 | "emoji", 13 | "quill", 14 | "editor" 15 | ], 16 | "homepage": "https://github.com/contentco/quill-inline-comment", 17 | "ignore": [ 18 | "**/.*", 19 | "node_modules", 20 | "bower_components", 21 | "test", 22 | "tests" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /demo/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentco/quill-inline-comment/b2247a66ff3490e5c0b4e24aa76436c6de36ac44/demo/.gitkeep -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 31 | 32 | 33 |
34 |

Editor

35 |
36 |
37 | 38 |
39 |

Debug

40 |
41 | 43 | 44 | 46 |
47 |
48 | 49 | 50 | 51 | 52 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /demo/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentco/quill-inline-comment/b2247a66ff3490e5c0b4e24aa76436c6de36ac44/demo/screenshot.png -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 31 | 32 | 33 |
34 |

Editor

35 |
36 |
37 | 38 |
39 |

Debug

40 |
41 | 43 | 44 | 46 |
47 |
48 | 49 | 50 | 51 | 52 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /dist/quill-inline-comment.css: -------------------------------------------------------------------------------- 1 | body{background:#fff}#quill-editor{position:relative}.inline-comment{background-color:#fff;border:1px solid #ccc;box-shadow:0 0 5px #ddd;color:#444;padding:5px 12px;white-space:nowrap}.commentText{border:none;display:block;resize:none}.commentText:focus{outline:none}.inline-comment-bottom{margin-top:15px;text-align:right}.annotation{background:#a5caf2} -------------------------------------------------------------------------------- /dist/quill-inline-comment.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=2)}([function(e,t,n){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(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}function i(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)}function l(e){var t=document.getElementById("inline-comment"),n=document.getElementById("inline-comment-mask");t?(t.remove(),n.remove()):c(e)}function c(e){var t=e.getSelection();if(!(e.getText(t.index,t.length).length<1)){var n=e.getBounds(t.index),o=document.createElement("div");o.id="inline-comment-mask",o.style.width="100%",o.style.height="100%",o.style.top="0px",o.style.position="fixed",o.style.display="block";var r=document.createElement("div");r.id="inline-comment",r.classList.add("inline-comment"),e.container.appendChild(r),e.container.appendChild(o),r.style.position="absolute",r.innerHTML='
Cancel Send
',r.style.left=n.left-250+"px",n.left+250'})}return a(e,[{key:"commentEventHanlder",value:function(){l(this.quill)}}]),e}();Quill.register("modules/inline_comment",d)},function(e,t){},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var r=n(1),i=(o(r),n(0));o(i)}]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quill-inline-comment", 3 | "version": "0.0.9", 4 | "description": "Quill Extension for Inline Comment", 5 | "main": "webpack.config.js", 6 | "devDependencies": { 7 | "babel-core": "^6.24.1", 8 | "babel-loader": "^6.4.1", 9 | "babel-preset-env": "^1.5.1", 10 | "babel-preset-es2015": "^6.24.1", 11 | "css-loader": "^0.27.3", 12 | "extract-text-webpack-plugin": "^2.1.0", 13 | "file-loader": "^0.11.2", 14 | "node-sass": "^4.5.3", 15 | "sass-loader": "^6.0.5", 16 | "uglifyjs-webpack-plugin": "^0.4.3", 17 | "url-loader": "^0.5.9", 18 | "webpack": "^2.6.1", 19 | "webpack-dev-server": "^2.4.5" 20 | }, 21 | "scripts": { 22 | "test": "npm test", 23 | "build": "webpack" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/contentco/quill-inline-comment.git" 28 | }, 29 | "keywords": [ 30 | "content", 31 | "quill", 32 | "editor" 33 | ], 34 | "author": "contentco", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/contentco/quill-inline-comment/issues" 38 | }, 39 | "homepage": "https://github.com/contentco/quill-inline-comment#readme", 40 | "dependencies": { 41 | "babel": "^6.23.0", 42 | "preact": "^7.2.1", 43 | "quill": "^1.2.4" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/module-inline-comment.js: -------------------------------------------------------------------------------- 1 | let Inline = Quill.import('blots/inline'); 2 | 3 | class CommentBlot extends Inline { 4 | static create(commentText) { 5 | const node = super.create(); 6 | node.dataset.comment = commentText.comment; 7 | if (commentText.id) { 8 | node.dataset.id = commentText.id; 9 | } 10 | if (commentText.resolved) { 11 | node.dataset.resolved = commentText.resolved; 12 | } 13 | 14 | return node; 15 | } 16 | static formats(node) { 17 | return node.dataset; 18 | } 19 | format(name, value) { 20 | super.format(name, value); 21 | } 22 | } 23 | 24 | CommentBlot.blotName = "comment"; 25 | CommentBlot.tagName = "SPAN"; 26 | CommentBlot.className = "annotation"; 27 | 28 | Quill.register({ 29 | 'formats/comment': CommentBlot 30 | }); 31 | 32 | 33 | class GrammlyBlot extends Inline { 34 | static create(commentText) { 35 | const node = super.create(); 36 | 37 | return node; 38 | } 39 | static formats(node) { 40 | return node.dataset; 41 | } 42 | format(name, value) { 43 | super.format(name, value); 44 | } 45 | } 46 | 47 | GrammlyBlot.blotName = "grammer"; 48 | GrammlyBlot.tagName = "SPAN"; 49 | GrammlyBlot.className = "gr_"; 50 | 51 | Quill.register({ 52 | 'formats/grammer': GrammlyBlot 53 | }); 54 | 55 | class InlineComment { 56 | constructor(quill){ 57 | this.quill = quill; 58 | this.toolbar = quill.getModule('toolbar'); 59 | if (typeof this.toolbar != 'undefined') 60 | this.toolbar.addHandler('comment', this.commentEventHanlder); 61 | 62 | var commentBtns = document.getElementsByClassName('ql-comment'); 63 | if (commentBtns) { 64 | [].slice.call( commentBtns ).forEach(function ( commentBtn ) { 65 | commentBtn.innerHTML = ''; 66 | }); 67 | }; 68 | } 69 | 70 | commentEventHanlder() { 71 | let quill = this.quill; 72 | checkDialogExist(quill); 73 | } 74 | } 75 | 76 | function checkDialogExist(quill){ 77 | let commentToolTip = document.getElementById("inline-comment"); 78 | let commentMask = document.getElementById("inline-comment-mask"); 79 | if (commentToolTip) { 80 | commentToolTip.remove(); 81 | commentMask.remove(); 82 | } 83 | else{ 84 | createCommentDialog(quill); 85 | } 86 | } 87 | 88 | function createCommentDialog(quill) { 89 | let range = quill.getSelection(); 90 | let text = quill.getText(range.index, range.length); 91 | if (text.length < 1) { 92 | return; 93 | } 94 | const atSignBounds = quill.getBounds(range.index); 95 | let containerMask = document.createElement('div'); 96 | containerMask.id="inline-comment-mask"; 97 | containerMask.style.width = "100%"; 98 | containerMask.style.height = "100%"; 99 | containerMask.style.top = "0px"; 100 | containerMask.style.position = "fixed"; 101 | containerMask.style.display = "block"; 102 | 103 | let container = document.createElement('div'); 104 | container.id = 'inline-comment'; 105 | container.classList.add('inline-comment'); 106 | quill.container.appendChild(container); 107 | quill.container.appendChild(containerMask); 108 | container.style.position = "absolute"; 109 | container.innerHTML = '
Cancel Send
'; 110 | 111 | 112 | container.style.left = (atSignBounds.left - 250)+ "px"; 113 | 114 | if (atSignBounds.left + 250 < quill.container.clientWidth) { 115 | container.style.left = (atSignBounds.left)+ "px"; 116 | } 117 | 118 | container.style.top = 10 + atSignBounds.top + atSignBounds.height + "px"; 119 | container.style.zIndex = 80; 120 | document.querySelector('.commentText').focus(); 121 | 122 | let inlineCancel = document.querySelector('.inline-cancel'); 123 | let commentToolTip = document.querySelector('.inline-comment'); 124 | 125 | inlineCancel.addEventListener('click',function(){ 126 | commentToolTip.style.display = "none"; 127 | containerMask.style.display = "none"; 128 | }); 129 | 130 | let inlineSend = document.querySelector('.inline-send'); 131 | 132 | inlineSend.addEventListener('click',function(){ 133 | // var commentId = 1; 134 | // var commentStatus = 'resolved'; 135 | const commentObj = {}; 136 | let commentText = document.querySelector('.commentText').value; 137 | commentObj.comment = commentText; 138 | if (typeof(commentId) !== 'undefined') { 139 | commentObj.id= commentId; 140 | } 141 | if (typeof(commentStatus) !== 'undefined') { 142 | commentObj.resolved= commentStatus; 143 | } 144 | commentToolTip.remove(); 145 | containerMask.remove(); 146 | //quill.format('comment', commentObj); 147 | quill.format('comment', commentObj); 148 | }); 149 | 150 | } 151 | 152 | Quill.register('modules/inline_comment', InlineComment); 153 | -------------------------------------------------------------------------------- /src/quill-inline-comment.js: -------------------------------------------------------------------------------- 1 | import css from './scss/base.scss'; 2 | import InlineComment from '../src/module-inline-comment'; -------------------------------------------------------------------------------- /src/scss/base.scss: -------------------------------------------------------------------------------- 1 | @import 'core/styles'; -------------------------------------------------------------------------------- /src/scss/core/_styles.scss: -------------------------------------------------------------------------------- 1 | @import 'core/variables'; /* sass-loader doesnt like absolute imports*/ 2 | body { 3 | background: $white; 4 | } 5 | #quill-editor { 6 | position: relative; 7 | } 8 | .inline-comment{ 9 | background-color: #fff; 10 | border: 1px solid #ccc; 11 | box-shadow: 0 0 5px #ddd; 12 | color: #444; 13 | padding: 5px 12px; 14 | white-space: nowrap; 15 | } 16 | .commentText{ 17 | border: none; 18 | display: block; 19 | resize: none; 20 | } 21 | .commentText:focus{ 22 | outline: none; 23 | } 24 | .inline-comment-bottom{ 25 | margin-top: 15px; 26 | text-align: right; 27 | } 28 | .annotation{ 29 | background: #a5caf2; 30 | } 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/scss/core/_variables.scss: -------------------------------------------------------------------------------- 1 | $white: #FFF; 2 | $blue: #0366d6; 3 | $cyan: #2D9EE0; 4 | $light-blue: #84a8cc; 5 | $light-gray: #ddd; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 3 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 4 | 5 | // const autoprefixer = requre('autoprefixer'); 6 | 7 | const config = { 8 | entry: './src/quill-inline-comment.js', 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | filename: 'quill-inline-comment.js' 12 | }, 13 | devServer: { 14 | contentBase: path.join(__dirname), 15 | compress: true, 16 | port: 9000 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.scss$/, 22 | use: ExtractTextPlugin.extract({ 23 | use: [{ 24 | loader: 'css-loader', 25 | options: { 26 | minimize: true || {/* CSSNano Options */} 27 | } 28 | }, { 29 | loader: 'sass-loader', 30 | }] 31 | }), 32 | }, 33 | { 34 | test: /\.js$/, 35 | include: [ 36 | path.resolve(__dirname, "src/") 37 | ], 38 | exclude: /(node_modules)/, 39 | use: { 40 | loader: 'babel-loader', 41 | options: { 42 | presets: [['es2015', {modules: false}],] 43 | } 44 | } 45 | }, 46 | { 47 | test: /\.png$/, 48 | loader: "file-loader" 49 | }, 50 | { 51 | test: /\.(png|woff|woff2|eot|ttf|svg)$/, 52 | loader: 'url-loader?limit=100000' 53 | } 54 | ] 55 | }, 56 | plugins: [ 57 | new ExtractTextPlugin('quill-inline-comment.css'), 58 | new UglifyJSPlugin({ 59 | compress: { 60 | warnings: false, 61 | screw_ie8: true, 62 | conditionals: true, 63 | unused: true, 64 | comparisons: true, 65 | sequences: true, 66 | dead_code: true, 67 | evaluate: true, 68 | join_vars: true, 69 | if_return: true 70 | }, 71 | output: { 72 | comments: false 73 | } 74 | }), 75 | ] 76 | }; 77 | 78 | module.exports = config; 79 | --------------------------------------------------------------------------------