activeadmin_quill_editor 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad04679c2c87aaf9cc8e2e8577ec9b9f216b91c0c61692f5261e126047ece83d
4
- data.tar.gz: 7afbcb26e9049f76a48582c4c8fbdf4e7a09a592a4a0cc95ffb023d47b12d01f
3
+ metadata.gz: a4c64ad878e1367dbc40efb644f4372e3cc85e726a2dafa9e602f16311f59ba3
4
+ data.tar.gz: c35421055ae083865000d9c02525e715f2a273502c60e026796233a686fce96f
5
5
  SHA512:
6
- metadata.gz: 6188c7952e753a9d6da501062d2e779c25759b2dd1441b8cf9475ecfa0d5e0e8d299e15f3ae8cfe745181539c710a7f670fd6b727d0b043781662eb730f77914
7
- data.tar.gz: 1d918e65c25b1771af61473fda8a961ba9d6b3d0f0dad3d5a598998684e79f065c0f04a94caf32e32ab02e11dfbffa216ad3f50a808933588a96764b04f9fc2b
6
+ metadata.gz: ae07acc5c4c31c1d67b7f53056b571c3daaad81d29793cb7a3cb79ae05cc2a8686cced1a899df4e204f9d18b4336d6e324d7daff2490bbe56b9696a7d8508b9a
7
+ data.tar.gz: a46ca361536d05ba54166df559448230c3283a3a9477ead220aece61cc54d6341d3af28fb7e01ac6fbf650e080721f7ca1c2096f78fb650da8d09411f52cb257
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # ActiveAdmin Quill Editor [![Gem Version](https://badge.fury.io/rb/activeadmin_quill_editor.svg)](https://badge.fury.io/rb/activeadmin_quill_editor) [![CircleCI](https://circleci.com/gh/blocknotes/activeadmin_quill_editor.svg?style=svg)](https://circleci.com/gh/blocknotes/activeadmin_quill_editor)
2
2
 
3
- An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill)
3
+ An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill) in form fields.
4
4
 
5
5
  ![screenshot](screenshot.png)
6
6
 
7
7
  ## Install
8
- - After installing Active Admin, add to your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
8
+ After installing Active Admin, add to your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
9
+
10
+ If you installed Active Admin without Webpacker support (default for now):
11
+
9
12
  - Add at the end of your Active Admin styles (_app/assets/stylesheets/active_admin.scss_):
10
13
  ```scss
11
14
  @import 'activeadmin/quill_editor/quill.snow';
@@ -16,13 +19,23 @@ An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quillj
16
19
  //= require activeadmin/quill_editor/quill
17
20
  //= require activeadmin/quill_editor_input
18
21
  ```
19
- - Use the input with `as: :quill_editor` in Active Admin model conf
20
-
21
- Why 2 separated scripts? In this way you can include a different version of *quill editor* if you like.
22
22
 
23
23
  > **UPDATE FROM VERSION <= 2.0**: please add to your _app/assets/stylesheets/active_admin.scss_ the line `@import 'activeadmin/quill_editor/quill.snow';`
24
24
 
25
- ## Options
25
+ If you installed Active Admin with Webpacker support:
26
+
27
+ - Execute in your project root:
28
+ ```sh
29
+ yarn add blocknotes/activeadmin_quill_editor
30
+ ```
31
+ - Add to your *app/javascript/packs/active_admin.js*:
32
+ ```js
33
+ require('activeadmin_quill_editor')
34
+ ```
35
+
36
+ ## Usage
37
+ In your Active Admin models, form configuration, set the text inputs with `as: :quill_editor` where needed.
38
+
26
39
  **data-options**: permits to set *quill editor* options directly - see [options list](https://quilljs.com/docs/configuration/)
27
40
 
28
41
  ## Examples
@@ -44,11 +57,34 @@ Why 2 separated scripts? In this way you can include a different version of *qui
44
57
  ### Toolbar buttons configuration
45
58
 
46
59
  ```ruby
47
- f.input :description, as: :quill_editor, input_html: {data: {options: {modules: {toolbar: [['bold', 'italic', 'underline'], ['link']]}, placeholder: 'Type something...', theme: 'snow'}}}
60
+ f.input :description, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: [['bold', 'italic', 'underline'], ['link']] }, placeholder: 'Type something...', theme: 'snow' } } }
61
+ ```
62
+
63
+ ### ImageUploader plugin
64
+ This plugin allows to upload images to the server (instead of storing them in *base64* by default), reference [here](https://github.com/NoelOConnell/quill-image-uploader).
65
+
66
+ ```ruby
67
+ # Upload method (to be included in the admin entity configuration)
68
+ member_action :upload, method: [:post] do
69
+ result = { success: resource.images.attach(params[:file_upload]) }
70
+ result[:url] = url_for(resource.images.last) if result[:success]
71
+ render json: result
72
+ end
73
+ ```
74
+
75
+ ```ruby
76
+ # Form field
77
+ unless object.new_record?
78
+ plugin_opts = { image_uploader: { server_url: upload_admin_post_path(object.id), field_name: 'file_upload' } }
79
+ f.input :description, as: :quill_editor, input_html: { data: { plugins: plugin_opts } }
80
+ end
48
81
  ```
49
82
 
50
- ## Notes
51
- - Upload functions (Images, Documents, Files, etc.) are not implemented yet
83
+ For the relevant files of the upload example see [here](examples/upload_plugin_using_activestorage/).
84
+ Consider that this is just a basic example: images are uploaded as soon as they are attached to the
85
+ editor (regardless of the form submit), it shows the editor only for an existing record (because of
86
+ the *upload_admin_post_path*) and it doesn't provide a way to remove images (just deleting them from
87
+ the editor will not destroy them, you'll need to implement a purge logic for that).
52
88
 
53
89
  ## Do you like it? Star it!
54
90
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
@@ -60,4 +96,4 @@ Take a look at [other Active Admin components](https://github.com/blocknotes?utf
60
96
  - The good guys that opened issues and pull requests from time to time
61
97
 
62
98
  ## License
63
- - The gem is available as open-source under the terms of the [MIT](LICENSE.txt)
99
+ The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
@@ -0,0 +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,45 +1,95 @@
1
- function initQuillEditors() {
2
- var editors = document.querySelectorAll('.quill-editor');
3
- var default_options = {
4
- modules: {
5
- toolbar: [
6
- ['bold', 'italic', 'underline'],
7
- ['link', 'blockquote', 'code-block'],
8
- [{ 'script': 'sub'}, { 'script': 'super' }],
9
- [{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
10
- [{ 'color': [] }, { 'background': [] }],
11
- ['clean'],
12
- ]
13
- },
14
- placeholder: '',
15
- theme: 'snow'
16
- };
17
-
18
- for(var i = 0; i < editors.length; i++) {
19
- var content = editors[i].querySelector('.quill-editor-content');
20
- var isActive = editors[i].classList.contains('quill-editor--active');
21
- if(content && !isActive) {
22
- var options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : default_options;
23
- editors[i]['_quill-editor'] = new Quill(content, options);
24
- editors[i].classList += ' quill-editor--active';
1
+ (function () {
2
+ 'use strict'
3
+
4
+ // --- functions ---------------------------------------------------------------
5
+ function initQuillEditors() {
6
+ let default_theme = 'snow'
7
+ let default_toolbar = [
8
+ ['bold', 'italic', 'underline'],
9
+ ['link', 'blockquote', 'code-block'],
10
+ [{ 'script': 'sub' }, { 'script': 'super' }],
11
+ [{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
12
+ [{ 'color': [] }, { 'background': [] }],
13
+ ['image'],
14
+ ['clean'],
15
+ ]
16
+ let editors = document.querySelectorAll('[data-aa-quill-editor]')
17
+ let registered_plugins = {}
18
+
19
+ for (let i = 0; i < editors.length; i++) {
20
+ let content = editors[i].querySelector('[data-aa-quill-content]')
21
+ let isActive = editors[i].classList.contains('quill-editor--active')
22
+ if (content && !isActive) {
23
+ // Setup editor options
24
+ let options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : {}
25
+ if (!options.theme) options.theme = default_theme
26
+ if (!options.modules) options.modules = {}
27
+ if (!options.modules.toolbar) options.modules.toolbar = default_toolbar
28
+
29
+ // Setup plugin options
30
+ let plugin_options = editors[i].getAttribute('data-plugins') ? JSON.parse(editors[i].getAttribute('data-plugins')) : {}
31
+ if (plugin_options.image_uploader && plugin_options.image_uploader.server_url) {
32
+ if (!registered_plugins.image_uploader) {
33
+ Quill.register('modules/imageUploader', ImageUploader)
34
+ registered_plugins.image_uploader = true
35
+ }
36
+ let opts = plugin_options.image_uploader
37
+ options.modules.imageUploader = setupImageUploader(opts.server_url, opts.field_name)
38
+ }
39
+
40
+ // Init editor
41
+ editors[i]['_quill-editor'] = new Quill(content, options)
42
+ editors[i].classList += ' quill-editor--active'
43
+ }
44
+ }
45
+
46
+ let formtastic = document.querySelector('form.formtastic')
47
+ if (formtastic) {
48
+ formtastic.onsubmit = () => {
49
+ for (let i = 0; i < editors.length; i++) {
50
+ let input = editors[i].querySelector('input[type="hidden"]')
51
+ if (editors[i]['_quill-editor'].editor.isBlank()) {
52
+ input.value = ''
53
+ } else {
54
+ input.value = editors[i]['_quill-editor'].root.innerHTML
55
+ }
56
+ }
57
+ };
25
58
  }
26
59
  }
27
60
 
28
- var formtastic = document.querySelector('form.formtastic');
29
- if(formtastic) {
30
- formtastic.onsubmit = function() {
31
- for(var i = 0; i < editors.length; i++) {
32
- var input = editors[i].querySelector('input[type="hidden"]');
33
- input.value = editors[i]['_quill-editor'].root.innerHTML;
61
+ function setupImageUploader(server_url, field_name) {
62
+ return {
63
+ upload: file => {
64
+ return new Promise((resolve, reject) => {
65
+ const formData = new FormData()
66
+ formData.append(field_name || 'file_upload', file)
67
+
68
+ fetch(server_url, {
69
+ body: formData,
70
+ headers: {
71
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
72
+ },
73
+ method: 'POST'
74
+ }).then(response => response.json())
75
+ .then(result => {
76
+ resolve(result.url);
77
+ })
78
+ .catch(error => {
79
+ reject('Upload failed')
80
+ console.error('Error: ', error)
81
+ })
82
+ })
34
83
  }
35
- };
84
+ }
36
85
  }
37
- }
38
86
 
39
- $(document).ready( function() {
40
- initQuillEditors();
41
- });
87
+ // --- events ------------------------------------------------------------------
88
+ $(document).ready(() => {
89
+ initQuillEditors()
90
+ })
42
91
 
43
- $(document).on('has_many_add:after', function() {
44
- initQuillEditors();
45
- });
92
+ $(document).on('has_many_add:after', '.has_many_container', () => {
93
+ initQuillEditors()
94
+ })
95
+ })()
@@ -1,4 +1,11 @@
1
- body.active_admin .quill-editor {
1
+ // reset internal elements
2
+ .ql-editor * {
3
+ margin: initial;
4
+ padding: initial;
5
+ text-align: initial;
6
+ }
7
+
8
+ body.active_admin [data-aa-quill-editor] {
2
9
  display: inline-block;
3
10
  width: calc(80% - 2px);
4
11
 
@@ -23,13 +30,6 @@ body.active_admin .quill-editor {
23
30
  min-height: 150px;
24
31
  padding: 10px;
25
32
 
26
- // reset internal elements
27
- * {
28
- margin: initial;
29
- padding: initial;
30
- text-align: initial;
31
- }
32
-
33
33
  ol {
34
34
  list-style-type: decimal;
35
35
  }
@@ -0,0 +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
+ }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveAdmin
4
4
  module QuillEditor
5
- VERSION = '0.2.8'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -6,9 +6,9 @@ module Formtastic
6
6
  def to_html
7
7
  input_wrapping do
8
8
  label_html <<
9
- template.content_tag(:div, input_html_options.merge(class: 'quill-editor')) do
9
+ template.content_tag(:div, input_html_options.merge('data-aa-quill-editor': '1')) do
10
10
  builder.hidden_field(input_name) <<
11
- template.content_tag(:div, class: 'quill-editor-content') do
11
+ template.content_tag(:div, 'data-aa-quill-content': '1') do
12
12
  object.send(method).try :html_safe
13
13
  end
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin_quill_editor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-03 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeadmin
@@ -159,11 +159,13 @@ files:
159
159
  - LICENSE.txt
160
160
  - README.md
161
161
  - Rakefile
162
+ - app/assets/javascripts/activeadmin/quill.imageUploader.min.js
162
163
  - app/assets/javascripts/activeadmin/quill_editor/quill.core.js
163
164
  - app/assets/javascripts/activeadmin/quill_editor/quill.js
164
165
  - app/assets/javascripts/activeadmin/quill_editor/quill.min.js
165
166
  - app/assets/javascripts/activeadmin/quill_editor_input.js
166
167
  - app/assets/stylesheets/activeadmin/_quill_editor_input.scss
168
+ - app/assets/stylesheets/activeadmin/quill.imageUploader.min.css
167
169
  - app/assets/stylesheets/activeadmin/quill_editor/quill.bubble.css
168
170
  - app/assets/stylesheets/activeadmin/quill_editor/quill.core.css
169
171
  - app/assets/stylesheets/activeadmin/quill_editor/quill.snow.css