зеркало из
https://github.com/rs-pro/activeadmin-quill_editor.git
synced 2026-08-28 15:36:17 +03:00
Сравнить коммиты
34 Коммитов
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
2d5429718c | ||
|
|
78126d0cd7 | ||
|
|
3190134973 | ||
|
|
8d6b05297a | ||
|
|
5d9b6f70fd | ||
|
|
6fa021c0c7 | ||
|
|
357ef382d6 | ||
|
|
43ae70c7c9 | ||
|
|
6daf6f6bc8 | ||
|
|
98c235d346 | ||
|
|
e6e6742753 | ||
|
|
c2c8d48795 | ||
|
|
72062c1a30 | ||
|
|
c4aa91225b | ||
|
|
3cc6edb302 | ||
|
|
d78fb2eb9c | ||
|
|
e5db64af96 | ||
|
|
c0d37ad94e | ||
|
|
96c70106a4 | ||
|
|
4ec6e80760 | ||
|
|
707f4a5cc7 | ||
|
|
dc97316303 | ||
|
|
e9b8abc88e | ||
|
|
582667dc59 | ||
|
|
76c06f5346 | ||
|
|
4e20e658ac | ||
|
|
135a56dc03 | ||
|
|
668bd642e5 | ||
|
|
ab8c4fd73f | ||
|
|
d60ed0f723 | ||
|
|
98b29d6f69 | ||
|
|
962b48bb30 | ||
|
|
03b2d0e4e8 | ||
|
|
593ad3841a |
33
.circleci/config.yml
Обычный файл
33
.circleci/config.yml
Обычный файл
@@ -0,0 +1,33 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
ruby: circleci/ruby@0.1.2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/ruby:2.6.6-stretch-node-browsers
|
||||
executor: ruby/default
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: gem-cache-{{ checksum "activeadmin_quill_editor.gemspec" }}
|
||||
- run:
|
||||
name: Setup Bundler
|
||||
command: gem install bundler
|
||||
- run:
|
||||
name: Bundle Install
|
||||
command: bundle install --path vendor/bundle
|
||||
- save_cache:
|
||||
key: gem-cache-{{ checksum "activeadmin_quill_editor.gemspec" }}
|
||||
paths: vendor/bundle
|
||||
- run:
|
||||
name: Run Specs
|
||||
command: |
|
||||
bin/rspec \
|
||||
--profile 10 \
|
||||
--format RspecJunitFormatter \
|
||||
--out test_results/rspec.xml \
|
||||
--format progress \
|
||||
$(circleci tests glob "spec/**/*_spec.rb")
|
||||
- store_test_results:
|
||||
path: test_results
|
||||
44
.github/workflows/brakeman-analysis.yml
поставляемый
Обычный файл
44
.github/workflows/brakeman-analysis.yml
поставляемый
Обычный файл
@@ -0,0 +1,44 @@
|
||||
# This workflow integrates Brakeman with GitHub's Code Scanning feature
|
||||
# Brakeman is a static analysis security vulnerability scanner for Ruby on Rails applications
|
||||
|
||||
name: Brakeman Scan
|
||||
|
||||
# This section configures the trigger for the workflow. Feel free to customize depending on your convention
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "main" ]
|
||||
pull_request:
|
||||
branches: [ "master", "main" ]
|
||||
|
||||
jobs:
|
||||
brakeman-scan:
|
||||
name: Brakeman Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout the repository to the GitHub Actions runner
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Customize the ruby version depending on your needs
|
||||
- name: Setup Ruby
|
||||
uses: actions/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '2.7'
|
||||
|
||||
- name: Setup Brakeman
|
||||
env:
|
||||
BRAKEMAN_VERSION: '4.10' # SARIF support is provided in Brakeman version 4.10+
|
||||
run: |
|
||||
gem install brakeman --version $BRAKEMAN_VERSION
|
||||
|
||||
# Execute Brakeman CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Scan
|
||||
continue-on-error: true
|
||||
run: |
|
||||
brakeman -f sarif -o output.sarif.json .
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: output.sarif.json
|
||||
2
.gitignore
поставляемый
2
.gitignore
поставляемый
@@ -2,10 +2,10 @@
|
||||
*.orig
|
||||
|
||||
/.rspec_failures
|
||||
/.rubocop-*
|
||||
/Gemfile.lock
|
||||
|
||||
/_misc/
|
||||
/coverage/
|
||||
/spec/dummy/db/*.sqlite3*
|
||||
/spec/dummy/log/
|
||||
/spec/dummy/storage/
|
||||
|
||||
1
.rspec
1
.rspec
@@ -1,3 +1,2 @@
|
||||
--require rails_helper
|
||||
--format documentation
|
||||
--profile
|
||||
|
||||
74
.rubocop.yml
74
.rubocop.yml
@@ -1,73 +1,27 @@
|
||||
require:
|
||||
- rubocop-rspec
|
||||
|
||||
Rails:
|
||||
Enabled: true
|
||||
inherit_from:
|
||||
- https://relaxed.ruby.style/rubocop.yml
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3.8
|
||||
TargetRailsVersion: 5.2
|
||||
Exclude:
|
||||
- db/schema.rb
|
||||
- bin/*
|
||||
- node_modules/**/*
|
||||
# Temporary files
|
||||
- tmp/**/*
|
||||
- spec/dummy/**/*
|
||||
NewCops: enable
|
||||
|
||||
Rails/InverseOf:
|
||||
Gemspec/RequiredRubyVersion:
|
||||
Enabled: false
|
||||
|
||||
Style/Documentation:
|
||||
Naming/FileName:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
# Default value is 100
|
||||
Max: 150
|
||||
|
||||
Metrics/LineLength:
|
||||
# Default is 80
|
||||
Layout/LineLength:
|
||||
Enabled: true
|
||||
Max: 120
|
||||
|
||||
Metrics/ModuleLength:
|
||||
# Default is 100
|
||||
Max: 150
|
||||
Style/HashEachMethods:
|
||||
Enabled: true
|
||||
|
||||
Metrics/ParameterLists:
|
||||
# Default is 5
|
||||
Max: 6
|
||||
Style/HashTransformKeys:
|
||||
Enabled: true
|
||||
|
||||
RSpec/ExampleLength:
|
||||
# Default is 10
|
||||
Max: 20
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
# Deface DOES edit strings in place
|
||||
Exclude:
|
||||
- 'app/overrides/**/*'
|
||||
|
||||
RSpec/MultipleExpectations:
|
||||
# Default is 3
|
||||
Max: 5
|
||||
|
||||
RSpec/NestedGroups:
|
||||
# Default is 3
|
||||
Max: 6
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 25
|
||||
|
||||
Metrics/BlockLength:
|
||||
# This value double the rubocop default
|
||||
Max: 50
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
# This value double the rubocop default
|
||||
Max: 12
|
||||
|
||||
Metrics/MethodLength:
|
||||
# This value double the rubocop default
|
||||
Max: 20
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
# Default is 7
|
||||
Max: 10
|
||||
Style/HashTransformValues:
|
||||
Enabled: true
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017 Mattia Roccoberton
|
||||
Copyright (c) 2017-2020 Mattia Roccoberton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
75
README.md
75
README.md
@@ -1,34 +1,49 @@
|
||||
# ActiveAdmin Quill Editor [](https://badge.fury.io/rb/activeadmin_quill_editor)
|
||||
# ActiveAdmin Quill Editor [](https://badge.fury.io/rb/activeadmin_quill_editor) [](https://circleci.com/gh/blocknotes/activeadmin_quill_editor)
|
||||
|
||||
An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill)
|
||||
An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill) in form fields.
|
||||
|
||||

|
||||

|
||||
|
||||
## Install
|
||||
After installing Active Admin, add to your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
|
||||
|
||||
- Update your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
|
||||
- Add at the end of your ActiveAdmin styles (_app/assets/stylesheets/active_admin.scss_):
|
||||
If you installed Active Admin without Webpacker support (default for now):
|
||||
|
||||
- Add at the end of your Active Admin styles (_app/assets/stylesheets/active_admin.scss_):
|
||||
```scss
|
||||
@import 'activeadmin/quill_editor/quill.snow';
|
||||
@import 'activeadmin/quill_editor_input';
|
||||
```
|
||||
- Add at the end of your ActiveAdmin javascripts (_app/assets/javascripts/active_admin.js_):
|
||||
- Add at the end of your Active Admin javascripts (_app/assets/javascripts/active_admin.js_):
|
||||
```js
|
||||
//= require activeadmin/quill_editor/quill
|
||||
//= require activeadmin/quill_editor_input
|
||||
```
|
||||
- Use the input with `as: :quill_editor` in Active Admin model conf
|
||||
|
||||
Why 2 separated scripts? In this way you can include a different version of *quill editor* if you like.
|
||||
> **UPDATE FROM VERSION <= 2.0**: please add to your _app/assets/stylesheets/active_admin.scss_ the line `@import 'activeadmin/quill_editor/quill.snow';`
|
||||
|
||||
## Options
|
||||
If you installed Active Admin with Webpacker support:
|
||||
|
||||
- Execute in your project root:
|
||||
```sh
|
||||
yarn add blocknotes/activeadmin_quill_editor
|
||||
```
|
||||
- Add to your *app/javascript/packs/active_admin.js*:
|
||||
```js
|
||||
require('activeadmin_quill_editor')
|
||||
```
|
||||
|
||||
## Usage
|
||||
In your Active Admin models, form configuration, set the text inputs with `as: :quill_editor` where needed.
|
||||
|
||||
**data-options**: permits to set *quill editor* options directly - see [options list](https://quilljs.com/docs/configuration/)
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic usage
|
||||
|
||||
```ruby
|
||||
# ActiveAdmin article form conf:
|
||||
# Active Admin article form conf:
|
||||
form do |f|
|
||||
f.inputs 'Article' do
|
||||
f.input :title
|
||||
@@ -39,26 +54,46 @@ Why 2 separated scripts? In this way you can include a different version of *qui
|
||||
end
|
||||
```
|
||||
|
||||
Toolbar buttons configuration:
|
||||
### Toolbar buttons configuration
|
||||
|
||||
```ruby
|
||||
f.input :description, as: :quill_editor, input_html: {data: {options: {modules: {toolbar: [['bold', 'italic', 'underline'], ['link']]}, placeholder: 'Type something...', theme: 'snow'}}}
|
||||
f.input :description, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: [['bold', 'italic', 'underline'], ['link']] }, placeholder: 'Type something...', theme: 'snow' } } }
|
||||
```
|
||||
|
||||
## Notes
|
||||
### ImageUploader plugin
|
||||
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).
|
||||
|
||||
- Upload functions (Images, Documents, Files, etc.) are not implemented yet
|
||||
```ruby
|
||||
# Upload method (to be included in the admin entity configuration)
|
||||
member_action :upload, method: [:post] do
|
||||
result = { success: resource.images.attach(params[:file_upload]) }
|
||||
result[:url] = url_for(resource.images.last) if result[:success]
|
||||
render json: result
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# Form field
|
||||
unless object.new_record?
|
||||
plugin_opts = { image_uploader: { server_url: upload_admin_post_path(object.id), field_name: 'file_upload' } }
|
||||
f.input :description, as: :quill_editor, input_html: { data: { plugins: plugin_opts } }
|
||||
end
|
||||
```
|
||||
|
||||
For the relevant files of the upload example see [here](examples/upload_plugin_using_activestorage/).
|
||||
Consider that this is just a basic example: images are uploaded as soon as they are attached to the
|
||||
editor (regardless of the form submit), it shows the editor only for an existing record (because of
|
||||
the *upload_admin_post_path*) and it doesn't provide a way to remove images (just deleting them from
|
||||
the editor will not destroy them, you'll need to implement a purge logic for that).
|
||||
|
||||
## Do you like it? Star it!
|
||||
If you use this component just star it. A developer is more motivated to improve a project when there is some interest. My other [Active Admin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source).
|
||||
|
||||
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
||||
|
||||
Take a look at [other ActiveAdmin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source) that I made if you are curious.
|
||||
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
|
||||
|
||||
## Contributors
|
||||
|
||||
- [Mattia Roccoberton](http://blocknot.es): author
|
||||
- The good guys that opened issues and pull requests from time to time
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE.txt)
|
||||
The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
|
||||
|
||||
13
Rakefile
13
Rakefile
@@ -1,3 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
|
||||
begin
|
||||
require 'rspec/core/rake_task'
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec) do |t|
|
||||
# t.ruby_opts = %w[-w]
|
||||
t.rspec_opts = ['--color', '--format documentation']
|
||||
end
|
||||
|
||||
task default: :spec
|
||||
rescue LoadError
|
||||
puts '! LoadError: no RSpec available'
|
||||
end
|
||||
|
||||
@@ -19,13 +19,15 @@ Gem::Specification.new do |spec|
|
||||
|
||||
spec.add_runtime_dependency 'activeadmin', '~> 2.0'
|
||||
|
||||
spec.add_development_dependency 'activestorage', '~> 6.0.3.2'
|
||||
spec.add_development_dependency 'capybara', '~> 3.33.0'
|
||||
spec.add_development_dependency 'pry', '~> 0.13.1'
|
||||
spec.add_development_dependency 'puma', '~> 4.3.5'
|
||||
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
|
||||
spec.add_development_dependency 'rspec-rails', '~> 4.0.1'
|
||||
spec.add_development_dependency 'selenium-webdriver', '~> 3.142.7'
|
||||
spec.add_development_dependency 'simplecov', '~> 0.19.0'
|
||||
spec.add_development_dependency 'sqlite3', '~> 1.4.2'
|
||||
spec.add_development_dependency 'activestorage', '~> 6.0'
|
||||
spec.add_development_dependency 'capybara', '~> 3.33'
|
||||
spec.add_development_dependency 'pry', '~> 0.13'
|
||||
spec.add_development_dependency 'puma', '~> 4.3'
|
||||
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4'
|
||||
spec.add_development_dependency 'rspec-rails', '~> 4.0'
|
||||
spec.add_development_dependency 'rubocop', '~> 0.90'
|
||||
spec.add_development_dependency 'sassc', '~> 2.4'
|
||||
spec.add_development_dependency 'selenium-webdriver', '~> 3.142'
|
||||
spec.add_development_dependency 'sprockets-rails', '~> 3.2'
|
||||
spec.add_development_dependency 'sqlite3', '~> 1.4'
|
||||
end
|
||||
|
||||
1
app/assets/javascripts/activeadmin/quill.imageUploader.min.js
поставляемый
Обычный файл
1
app/assets/javascripts/activeadmin/quill.imageUploader.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -1,45 +1,91 @@
|
||||
function initQuillEditors() {
|
||||
var editors = document.querySelectorAll('.quill-editor');
|
||||
var default_options = {
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline'],
|
||||
['link', 'blockquote', 'code-block'],
|
||||
[{ 'script': 'sub'}, { 'script': 'super' }],
|
||||
[{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
['clean'],
|
||||
]
|
||||
},
|
||||
placeholder: '',
|
||||
theme: 'snow'
|
||||
};
|
||||
(function () {
|
||||
'use strict'
|
||||
|
||||
for(var i = 0; i < editors.length; i++) {
|
||||
var content = editors[i].querySelector('.quill-editor-content');
|
||||
var isActive = editors[i].classList.contains('quill-editor--active');
|
||||
if(content && !isActive) {
|
||||
var options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : default_options;
|
||||
editors[i]['_quill-editor'] = new Quill(content, options);
|
||||
editors[i].classList += ' quill-editor--active';
|
||||
// --- functions ---------------------------------------------------------------
|
||||
function initQuillEditors() {
|
||||
let default_theme = 'snow'
|
||||
let default_toolbar = [
|
||||
['bold', 'italic', 'underline'],
|
||||
['link', 'blockquote', 'code-block'],
|
||||
[{ 'script': 'sub' }, { 'script': 'super' }],
|
||||
[{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
['image'],
|
||||
['clean'],
|
||||
]
|
||||
let editors = document.querySelectorAll('[data-aa-quill-editor]')
|
||||
let registered_plugins = {}
|
||||
|
||||
for (let i = 0; i < editors.length; i++) {
|
||||
let content = editors[i].querySelector('[data-aa-quill-content]')
|
||||
let isActive = editors[i].classList.contains('quill-editor--active')
|
||||
if (content && !isActive) {
|
||||
// Setup editor options
|
||||
let options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : {}
|
||||
if (!options.theme) options.theme = default_theme
|
||||
if (!options.modules) options.modules = {}
|
||||
if (!options.modules.toolbar) options.modules.toolbar = default_toolbar
|
||||
|
||||
// Setup plugin options
|
||||
let plugin_options = editors[i].getAttribute('data-plugins') ? JSON.parse(editors[i].getAttribute('data-plugins')) : {}
|
||||
if (plugin_options.image_uploader && plugin_options.image_uploader.server_url) {
|
||||
if (!registered_plugins.image_uploader) {
|
||||
Quill.register('modules/imageUploader', ImageUploader)
|
||||
registered_plugins.image_uploader = true
|
||||
}
|
||||
let opts = plugin_options.image_uploader
|
||||
options.modules.imageUploader = setupImageUploader(opts.server_url, opts.field_name)
|
||||
}
|
||||
|
||||
// Init editor
|
||||
editors[i]['_quill-editor'] = new Quill(content, options)
|
||||
editors[i].classList += ' quill-editor--active'
|
||||
}
|
||||
}
|
||||
|
||||
let formtastic = document.querySelector('form.formtastic')
|
||||
if (formtastic) {
|
||||
formtastic.onsubmit = () => {
|
||||
for (let i = 0; i < editors.length; i++) {
|
||||
let input = editors[i].querySelector('input[type="hidden"]')
|
||||
if (editors[i]['_quill-editor'].editor.isBlank()) {
|
||||
input.value = ''
|
||||
} else {
|
||||
input.value = editors[i]['_quill-editor'].root.innerHTML
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var formtastic = document.querySelector('form.formtastic');
|
||||
if(formtastic) {
|
||||
formtastic.onsubmit = function() {
|
||||
for(var i = 0; i < editors.length; i++) {
|
||||
var input = editors[i].querySelector('input[type="hidden"]');
|
||||
input.value = editors[i]['_quill-editor'].root.innerHTML;
|
||||
function setupImageUploader(server_url, field_name) {
|
||||
return {
|
||||
upload: file => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const formData = new FormData()
|
||||
formData.append(field_name || 'file_upload', file)
|
||||
|
||||
fetch(server_url, {
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
method: 'POST'
|
||||
}).then(response => response.json())
|
||||
.then(result => {
|
||||
resolve(result.url);
|
||||
})
|
||||
.catch(error => {
|
||||
reject('Upload failed')
|
||||
console.error('Error: ', error)
|
||||
})
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready( function() {
|
||||
initQuillEditors();
|
||||
});
|
||||
|
||||
$(document).on('has_many_add:after', function() {
|
||||
initQuillEditors();
|
||||
});
|
||||
// --- events ------------------------------------------------------------------
|
||||
$(document).ready(initQuillEditors)
|
||||
$(document).on('has_many_add:after', '.has_many_container', initQuillEditors)
|
||||
$(document).on('turbolinks:load', initQuillEditors)
|
||||
})()
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
body.active_admin .quill-editor {
|
||||
display: inline-block;
|
||||
width: calc(80% - 22px);
|
||||
// reset internal elements
|
||||
.ql-editor * {
|
||||
margin: initial;
|
||||
padding: initial;
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-image: none;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
body.active_admin [data-aa-quill-editor] {
|
||||
display: inline-block;
|
||||
width: calc(80% - 2px);
|
||||
|
||||
button {
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
background-image: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
border: 1px solid #c9d0d6;
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
|
||||
.ql-editor {
|
||||
@@ -12,13 +29,7 @@ body.active_admin .quill-editor {
|
||||
max-height: 300px;
|
||||
min-height: 150px;
|
||||
padding: 10px;
|
||||
|
||||
// reset internal elements
|
||||
* {
|
||||
margin: initial;
|
||||
padding: initial;
|
||||
text-align: initial;
|
||||
}
|
||||
word-break: break-all;
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
@@ -37,4 +48,9 @@ body.active_admin .quill-editor {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.ql-toolbar {
|
||||
border: 1px solid #c9d0d6;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
33
app/assets/stylesheets/activeadmin/quill.imageUploader.min.css
поставляемый
Обычный файл
33
app/assets/stylesheets/activeadmin/quill.imageUploader.min.css
поставляемый
Обычный файл
@@ -0,0 +1,33 @@
|
||||
.image-uploading {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.image-uploading img {
|
||||
max-width: 98% !important;
|
||||
filter: blur(5px);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.image-uploading::before {
|
||||
content: "";
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-top: -15px;
|
||||
margin-left: -15px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid #ccc;
|
||||
border-top-color: #1e986c;
|
||||
z-index: 1;
|
||||
animation: spinner 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spinner {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
29
bin/rubocop
Исполняемый файл
29
bin/rubocop
Исполняемый файл
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rubocop' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("rubocop", "rubocop")
|
||||
73
examples/upload_plugin_using_activestorage/app/admin/posts.rb
Обычный файл
73
examples/upload_plugin_using_activestorage/app/admin/posts.rb
Обычный файл
@@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ActiveAdmin.register Post do
|
||||
permit_params :author_id,
|
||||
:title,
|
||||
:description,
|
||||
:category,
|
||||
:dt,
|
||||
:position,
|
||||
:published,
|
||||
tag_ids: []
|
||||
|
||||
member_action :upload, method: [:post] do
|
||||
result = { success: resource.images.attach(params[:file_upload]) }
|
||||
result[:url] = url_for(resource.images.last) if result[:success]
|
||||
render json: result
|
||||
end
|
||||
|
||||
index do
|
||||
selectable_column
|
||||
id_column
|
||||
column :title
|
||||
column :author
|
||||
column :published
|
||||
column :created_at
|
||||
actions
|
||||
end
|
||||
|
||||
show do
|
||||
attributes_table do
|
||||
row :author
|
||||
row :title
|
||||
row :description
|
||||
row :category
|
||||
row :dt
|
||||
row :position
|
||||
row :published
|
||||
row :tags
|
||||
row :created_at
|
||||
row :updated_at
|
||||
row :images do |resurce|
|
||||
resurce.images.each do |image|
|
||||
div do
|
||||
link_to image.filename, image, target: '_blank'
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
active_admin_comments
|
||||
end
|
||||
|
||||
form do |f|
|
||||
f.inputs 'Post' do
|
||||
f.input :author
|
||||
f.input :title
|
||||
unless object.new_record?
|
||||
plugin_opts = { image_uploader: { server_url: upload_admin_post_path(object.id), field_name: 'file_upload' } }
|
||||
f.input :description, as: :quill_editor, input_html: { data: { plugins: plugin_opts } }
|
||||
end
|
||||
f.input :category
|
||||
f.input :dt
|
||||
f.input :position
|
||||
f.input :published
|
||||
end
|
||||
|
||||
f.inputs 'Tags' do
|
||||
f.input :tags
|
||||
end
|
||||
|
||||
f.actions
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
//= require active_admin/base
|
||||
|
||||
//= require activeadmin/quill_editor/quill
|
||||
//= require activeadmin/quill_editor_input
|
||||
|
||||
//= require activeadmin/quill.imageUploader.min
|
||||
@@ -0,0 +1,7 @@
|
||||
@import 'active_admin/mixins';
|
||||
@import 'active_admin/base';
|
||||
|
||||
@import 'activeadmin/quill_editor/quill.snow';
|
||||
@import 'activeadmin/quill_editor_input';
|
||||
|
||||
@import 'activeadmin/quill.imageUploader.min';
|
||||
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Post < ApplicationRecord
|
||||
has_many_attached :images
|
||||
|
||||
validates :title, allow_blank: false, presence: true
|
||||
end
|
||||
7
index.js
Обычный файл
7
index.js
Обычный файл
@@ -0,0 +1,7 @@
|
||||
// Styles
|
||||
import 'activeadmin_quill_editor/app/assets/stylesheets/activeadmin/quill_editor/quill.snow'
|
||||
import 'activeadmin_quill_editor/app/assets/stylesheets/activeadmin/_quill_editor_input'
|
||||
|
||||
// JS
|
||||
window.Quill = require('activeadmin_quill_editor/app/assets/javascripts/activeadmin/quill_editor/quill')
|
||||
require('activeadmin_quill_editor/app/assets/javascripts/activeadmin/quill_editor_input')
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
module ActiveAdmin
|
||||
module QuillEditor
|
||||
VERSION = '0.2.4'
|
||||
VERSION = '0.3.4'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,9 +6,9 @@ module Formtastic
|
||||
def to_html
|
||||
input_wrapping do
|
||||
label_html <<
|
||||
template.content_tag(:div, input_html_options.merge(class: 'quill-editor')) do
|
||||
template.content_tag(:div, input_html_options.merge('data-aa-quill-editor': '1')) do
|
||||
builder.hidden_field(input_name) <<
|
||||
template.content_tag(:div, class: 'quill-editor-content') do
|
||||
template.content_tag(:div, 'data-aa-quill-content': '1') do
|
||||
object.send(method).try :html_safe
|
||||
end
|
||||
end
|
||||
|
||||
13
package.json
Обычный файл
13
package.json
Обычный файл
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "activeadmin_quill_editor",
|
||||
"version": "0.3.4",
|
||||
"description": "Quill Editor for ActiveAdmin",
|
||||
"author": "Mattia Roccoberton <mat@blocknot.es>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/blocknotes/activeadmin_quill_editor",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"app/**/*",
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
Двоичные данные
screenshot.jpg
Двоичные данные
screenshot.jpg
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 33 KiB |
Двоичные данные
screenshot.png
Обычный файл
Двоичные данные
screenshot.png
Обычный файл
Двоичный файл не отображается.
|
После Ширина: | Высота: | Размер: 52 KiB |
@@ -1,9 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ActiveAdmin.register Author do
|
||||
menu priority: 1
|
||||
|
||||
permit_params :name, :email, :age, :avatar, profile_attributes: %i[id description _destroy]
|
||||
permit_params :name,
|
||||
:email,
|
||||
:age,
|
||||
:avatar,
|
||||
profile_attributes: %i[id description _destroy],
|
||||
posts_attributes: %i[id title description]
|
||||
|
||||
index do
|
||||
selectable_column
|
||||
@@ -28,6 +31,7 @@ ActiveAdmin.register Author do
|
||||
row :created_at
|
||||
row :updated_at
|
||||
row :profile
|
||||
row :posts
|
||||
end
|
||||
active_admin_comments
|
||||
end
|
||||
@@ -44,6 +48,10 @@ ActiveAdmin.register Author do
|
||||
f.has_many :profile, allow_destroy: true do |ff|
|
||||
ff.input :description
|
||||
end
|
||||
f.has_many :posts do |fp|
|
||||
fp.input :title
|
||||
fp.input :description, as: :quill_editor
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,15 +25,24 @@ ActiveAdmin.register Post do
|
||||
row :tags
|
||||
row :created_at
|
||||
row :updated_at
|
||||
row :images do |resurce|
|
||||
resurce.images.each do |image|
|
||||
div do
|
||||
link_to image.filename, image, target: '_blank'
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
active_admin_comments
|
||||
end
|
||||
|
||||
form do |f|
|
||||
toolbar = %w[bold italic underline link]
|
||||
f.inputs 'Post' do
|
||||
f.input :author
|
||||
f.input :title
|
||||
f.input :description, as: :quill_editor
|
||||
f.input :description, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: toolbar } } } }
|
||||
f.input :category
|
||||
f.input :dt
|
||||
f.input :position
|
||||
|
||||
@@ -12,6 +12,7 @@ class Author < ApplicationRecord
|
||||
has_one_attached :avatar
|
||||
|
||||
accepts_nested_attributes_for :profile, allow_destroy: true
|
||||
accepts_nested_attributes_for :posts, allow_destroy: true
|
||||
|
||||
validates :email, format: { with: /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\z/i, message: 'Invalid email' }
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ class Post < ApplicationRecord
|
||||
has_many :post_tags, inverse_of: :post, dependent: :destroy
|
||||
has_many :tags, through: :post_tags
|
||||
|
||||
has_many_attached :images
|
||||
|
||||
accepts_nested_attributes_for :post_tags, allow_destroy: true
|
||||
|
||||
validates :title, allow_blank: false, presence: true
|
||||
|
||||
scope :published, -> { where(published: true) }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require_relative 'boot'
|
||||
|
||||
require 'rails/all'
|
||||
require 'sprockets/railtie'
|
||||
|
||||
Bundler.require(*Rails.groups)
|
||||
|
||||
|
||||
@@ -13,12 +13,6 @@ require 'capybara/rails'
|
||||
|
||||
Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
|
||||
|
||||
require 'simplecov'
|
||||
SimpleCov.start :rails do
|
||||
filters.clear
|
||||
add_filter %r{^/spec/}
|
||||
end
|
||||
|
||||
# Force deprecations to raise an exception.
|
||||
ActiveSupport::Deprecation.behavior = :raise
|
||||
|
||||
|
||||
@@ -9,20 +9,66 @@ RSpec.describe 'Quill editor', type: :system do
|
||||
end
|
||||
|
||||
after do
|
||||
post.destroy
|
||||
author.destroy
|
||||
Post.delete_all
|
||||
author.delete
|
||||
end
|
||||
|
||||
context 'with a Quill editor' do
|
||||
it 'updates some HTML content' do
|
||||
it 'initialize the editor' do
|
||||
visit "/admin/posts/#{post.id}/edit"
|
||||
|
||||
%w[bold italic underline link].each do |button|
|
||||
expect(page).to have_css(".ql-toolbar button.ql-#{button}")
|
||||
end
|
||||
expect(page).to have_css('#post_description[data-aa-quill-editor]')
|
||||
expect(page).to have_css('#post_description_input .ql-editor', text: 'Some content...')
|
||||
find('#post_description_input .ql-editor').base.send_keys('more text')
|
||||
end
|
||||
|
||||
it 'adds some text to the description' do
|
||||
visit "/admin/posts/#{post.id}/edit"
|
||||
|
||||
find('#post_description_input .ql-editor').click
|
||||
find('#post_description_input .ql-editor').base.send_keys('more text')
|
||||
find('[type="submit"]').click
|
||||
expect(page).to have_content('was successfully updated')
|
||||
expect(post.reload.description).to eq '<p>Some content...more text</p>'
|
||||
end
|
||||
|
||||
it 'adds some bold text to the description' do
|
||||
visit "/admin/posts/#{post.id}/edit"
|
||||
|
||||
find('#post_description_input .ql-editor').click
|
||||
find('#post_description_input .ql-toolbar .ql-bold').click
|
||||
find('#post_description_input .ql-editor').base.send_keys('more text')
|
||||
find('[type="submit"]').click
|
||||
expect(post.reload.description).to eq '<p>Some content...<strong>more text</strong></p>'
|
||||
end
|
||||
|
||||
it 'adds some italic text to the description' do
|
||||
visit "/admin/posts/#{post.id}/edit"
|
||||
|
||||
find('#post_description_input .ql-editor').click
|
||||
find('#post_description_input .ql-toolbar .ql-italic').click
|
||||
find('#post_description_input .ql-editor').base.send_keys('more text')
|
||||
find('[type="submit"]').click
|
||||
expect(post.reload.description).to eq '<p>Some content...<em>more text</em></p>'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a Quill editor in a nested resource' do
|
||||
it 'updates some HTML content of a new nested resource' do
|
||||
visit "/admin/authors/#{author.id}/edit"
|
||||
|
||||
expect(page).to have_css('.posts.has_many_container .ql-editor', text: 'Some content...')
|
||||
find('.posts.has_many_container .has_many_add').click
|
||||
expect(page).to have_css('.posts.has_many_container .ql-editor', count: 2)
|
||||
|
||||
fill_in('author[posts_attributes][1][title]', with: 'A new post')
|
||||
find('#author_posts_attributes_1_description_input .ql-editor').base.send_keys('new post text')
|
||||
find('[type="submit"]').click
|
||||
|
||||
expect(page).to have_content('was successfully updated')
|
||||
expect(author.posts.last.description).to eq '<p>new post text</p>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Ссылка в новой задаче
Block a user