зеркало из
https://github.com/rs-pro/activeadmin-quill_editor.git
synced 2026-08-28 15:36:17 +03:00
Сравнить коммиты
160 Коммитов
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
13e2e83f18 | ||
|
|
c4a0fd2d17 | ||
|
|
1a6a7c6878 | ||
|
|
39380e7276 | ||
|
|
0a12969370 | ||
|
|
10ea2928a4 | ||
|
|
22e8fb58da | ||
|
|
e5035f96b5 | ||
|
|
c7a2fec052 | ||
|
|
74cbb7853d | ||
|
|
cd97f2eeb8 | ||
|
|
9d11acb6ec | ||
|
|
4c8d511bf6 | ||
|
|
96166aaf12 | ||
|
|
c26aef65ab | ||
|
|
194518dea4 | ||
|
|
395a099a3c | ||
|
|
826a3dee1c | ||
|
|
21d1395b77 | ||
|
|
d588651863 | ||
|
|
7f3d842e3a | ||
|
|
f291f7e7e6 | ||
|
|
df944277b5 | ||
|
|
3cc5683d44 | ||
|
|
498cbbd0c9 | ||
|
|
bfc2e997f1 | ||
|
|
9bda446762 | ||
|
|
533471c0d6 | ||
|
|
3617aab7f2 | ||
|
|
5196a8811a | ||
|
|
5e9a3b6c5a | ||
|
|
181aab7fd6 | ||
|
|
300b4a0a03 | ||
|
|
451a225277 | ||
|
|
6cbb39daad | ||
|
|
d19fc7ddfc | ||
|
|
74790fc115 | ||
|
|
c3b129a212 | ||
|
|
ab858f7c6f | ||
|
|
6cf744b84c | ||
|
|
0ae4ce2035 | ||
|
|
dd9aa22cf5 | ||
|
|
b6f3c0c3a6 | ||
|
|
7d3a084977 | ||
|
|
1569b1ff18 | ||
|
|
3459800dbf | ||
|
|
af901f4f7d | ||
|
|
328ee0d9fa | ||
|
|
751196ef8c | ||
|
|
589b782866 | ||
|
|
554f6086e7 | ||
|
|
051ada9843 | ||
|
|
fd90851a6b | ||
|
|
bd32c5f28f | ||
|
|
21ec7a918b | ||
|
|
b310f9e71b | ||
|
|
bbee6f7681 | ||
|
|
57e151aa03 | ||
|
|
0913bb6915 | ||
|
|
728e3fa097 | ||
|
|
9d891f0602 | ||
|
|
21fc69c2c0 | ||
|
|
b0e770ca9e | ||
|
|
ba53f9febf | ||
|
|
d690b9906f | ||
|
|
99908f97f7 | ||
|
|
9389f9e32d | ||
|
|
4629ac4133 | ||
|
|
0d4485c321 | ||
|
|
0c1cfefa83 | ||
|
|
0c674025a9 | ||
|
|
770e069442 | ||
|
|
d346729cd1 | ||
|
|
ce65cfbb7e | ||
|
|
22a1c8714c | ||
|
|
489e1c35eb | ||
|
|
62efca80d6 | ||
|
|
46168cc7f8 | ||
|
|
02310804aa | ||
|
|
7e2f8733dc | ||
|
|
2d0a8f961f | ||
|
|
7262ad5523 | ||
|
|
71f227736d | ||
|
|
361da5096d | ||
|
|
17ef9aa5db | ||
|
|
b00fcd90ab | ||
|
|
edf5cbcb13 | ||
|
|
38e5406d5d | ||
|
|
c560dd5c60 | ||
|
|
597cae7adb | ||
|
|
6d0599c39e | ||
|
|
664765ac79 | ||
|
|
7be390701f | ||
|
|
e4401a469b | ||
|
|
d8632f069a | ||
|
|
7498cd65a1 | ||
|
|
038d66f387 | ||
|
|
7af25106a2 | ||
|
|
d021f3650e | ||
|
|
8a13dd49a4 | ||
|
|
1cad5a10c3 | ||
|
|
63b4c118f6 | ||
|
|
277d1a521a | ||
|
|
4a0ea5e8f2 | ||
|
|
ffc57e7d90 | ||
|
|
2ebc9176b4 | ||
|
|
0b44c683e6 | ||
|
|
9b460d36e3 | ||
|
|
d41f8450ea | ||
|
|
401d2528e8 | ||
|
|
e086bac0a7 | ||
|
|
36cd836ca8 | ||
|
|
c05666673e | ||
|
|
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 | ||
|
|
d96162fedd | ||
|
|
a43a4ff0ba | ||
|
|
7a148c7cd1 | ||
|
|
5c462f5d7c | ||
|
|
9ecfceaa9a | ||
|
|
598b2d75cf | ||
|
|
54026744c4 | ||
|
|
5d35feedd8 | ||
|
|
73ee9e3787 | ||
|
|
b251a28b13 | ||
|
|
d3f2191843 | ||
|
|
0dfb48aec2 | ||
|
|
940ba2e9ca |
7
.fasterer.yml
Обычный файл
7
.fasterer.yml
Обычный файл
@@ -0,0 +1,7 @@
|
||||
---
|
||||
exclude_paths:
|
||||
- bin/*
|
||||
- db/schema.rb
|
||||
- gemfiles/**/*
|
||||
- spec/dummy/**/*
|
||||
- vendor/**/*
|
||||
13
.github/FUNDING.yml
поставляемый
Обычный файл
13
.github/FUNDING.yml
поставляемый
Обычный файл
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [blocknotes]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
84
.github/workflows/ci.yml
поставляемый
Обычный файл
84
.github/workflows/ci.yml
поставляемый
Обычный файл
@@ -0,0 +1,84 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby: ['3.2', '3.3', '3.4']
|
||||
gemfile:
|
||||
- rails_7.2_active_admin_4.x
|
||||
- rails_8.0_active_admin_4.x
|
||||
|
||||
env:
|
||||
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
||||
RAILS_ENV: test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: spec/dummy/package-lock.json
|
||||
|
||||
- name: Install npm dependencies for dummy app
|
||||
run: |
|
||||
cd spec/dummy
|
||||
npm install
|
||||
|
||||
- name: Build assets for dummy app
|
||||
run: |
|
||||
cd spec/dummy
|
||||
# Build JavaScript with esbuild
|
||||
npm run build:js || true
|
||||
# Build CSS with Tailwind
|
||||
npm run build:css || true
|
||||
|
||||
- name: Database setup
|
||||
run: |
|
||||
cd spec/dummy
|
||||
bundle exec rails db:create db:migrate db:test:prepare
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rspec --format progress
|
||||
|
||||
- name: Upload screenshots on failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-screenshots-${{ matrix.ruby }}-${{ matrix.gemfile }}
|
||||
path: spec/dummy/tmp/capybara
|
||||
if-no-files-found: ignore
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '3.4'
|
||||
bundler-cache: true
|
||||
|
||||
- name: Run RuboCop
|
||||
run: bundle exec rubocop --force-exclusion
|
||||
18
.gitignore
поставляемый
18
.gitignore
поставляемый
@@ -1,3 +1,17 @@
|
||||
_misc/
|
||||
|
||||
*.gem
|
||||
*.orig
|
||||
|
||||
/.rspec_failures
|
||||
/.rubocop-*
|
||||
/Gemfile.lock
|
||||
|
||||
/_misc/
|
||||
/coverage/
|
||||
/spec/dummy/db/*.sqlite3*
|
||||
/spec/dummy/db/schema-dev.rb
|
||||
/spec/dummy/log/
|
||||
/spec/dummy/storage/
|
||||
/spec/dummy/tmp/
|
||||
tmp/
|
||||
/spec/dummy/node_modules/
|
||||
/gemfiles/*.gemfile.lock
|
||||
|
||||
13
.overcommit.yml
Обычный файл
13
.overcommit.yml
Обычный файл
@@ -0,0 +1,13 @@
|
||||
CommitMsg:
|
||||
CapitalizedSubject:
|
||||
enabled: false
|
||||
|
||||
MessageFormat:
|
||||
enabled: true
|
||||
pattern: "^(fixup! ){0,1}(fix|feat|build|chore|ci|docs|style|refactor|perf|test)[!]{0,1}: .+$"
|
||||
expected_pattern_message: "<Commit type>: <Commit Message>"
|
||||
sample_message: "chore: add Overcommit config"
|
||||
|
||||
TextWidth:
|
||||
enabled: true
|
||||
max_subject_width: 72
|
||||
8
.reviewdog.yml
Обычный файл
8
.reviewdog.yml
Обычный файл
@@ -0,0 +1,8 @@
|
||||
---
|
||||
runner:
|
||||
fasterer:
|
||||
cmd: bin/fasterer
|
||||
level: info
|
||||
rubocop:
|
||||
cmd: bin/rubocop
|
||||
level: info
|
||||
2
.rspec
Обычный файл
2
.rspec
Обычный файл
@@ -0,0 +1,2 @@
|
||||
--require rails_helper
|
||||
--format documentation
|
||||
70
.rubocop.yml
Обычный файл
70
.rubocop.yml
Обычный файл
@@ -0,0 +1,70 @@
|
||||
---
|
||||
plugins:
|
||||
- rubocop-capybara
|
||||
- rubocop-packaging
|
||||
- rubocop-performance
|
||||
- rubocop-rails
|
||||
- rubocop-rspec
|
||||
- rubocop-rspec_rails
|
||||
|
||||
inherit_mode:
|
||||
merge:
|
||||
- Exclude
|
||||
|
||||
AllCops:
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 3.2
|
||||
SuggestExtensions: false
|
||||
DisplayCopNames: true
|
||||
CacheRootDirectory: tmp/rubocop-cache
|
||||
Exclude:
|
||||
- bin/*
|
||||
- coverage/**/*
|
||||
- db/schema.rb
|
||||
- docs/**/*
|
||||
- examples/**/*
|
||||
- gemfiles/**/*
|
||||
- spec/dummy/**/*
|
||||
- vendor/**/*
|
||||
|
||||
Layout/LineLength:
|
||||
Max: 120
|
||||
|
||||
Metrics/BlockLength:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- spec/**/*
|
||||
- docs/**/*
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 20
|
||||
Exclude:
|
||||
- spec/**/*
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
Style/StringLiterals:
|
||||
EnforcedStyle: single_quotes
|
||||
|
||||
Style/StringLiteralsInInterpolation:
|
||||
EnforcedStyle: double_quotes
|
||||
|
||||
RSpec/ExampleLength:
|
||||
Max: 12
|
||||
Exclude:
|
||||
- spec/system/**/*
|
||||
|
||||
RSpec/MultipleExpectations:
|
||||
Max: 4
|
||||
Exclude:
|
||||
- spec/system/**/*
|
||||
|
||||
RSpec/MultipleMemoizedHelpers:
|
||||
Max: 10
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: true
|
||||
|
||||
Rails/ActiveRecordAliases:
|
||||
Enabled: true
|
||||
19
AGENTS.md
Обычный файл
19
AGENTS.md
Обычный файл
@@ -0,0 +1,19 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
The gem code lives in `lib/activeadmin` and `lib/formtastic`, with the public entry point in `lib/activeadmin_quill_editor.rb`. Ruby assets and templates that Active Admin mounts ship from `app/assets` and `app/views`. Front-end bundles exposed to consuming apps sit in `index.js` and the `app/assets/javascripts/activeadmin/quill_editor` tree. System specs exercise the dummy Rails app under `spec/dummy`, while reusable page helpers reside in `spec/page_objects`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
`make up` builds the Docker environment defined in `extra/docker-compose.yml` and prepares the dummy app; add `RUBY`, `RAILS`, or `ACTIVEADMIN` env vars to test matrix combinations. Run `make specs` (or directly `bin/rspec --fail-fast`) to execute the suite, and `make lint` to invoke `bin/rubocop` inside the container. Use `make shell` for interactive debugging and `make server` to boot the dummy Rails instance at `SERVER_PORT`.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
Follow the `.rubocop.yml` rules: two-space indentation, trailing commas on multiline literals, double quotes where interpolation occurs, and `snake_case` for Ruby identifiers. Use `CamelCase` for classes and modules under the `ActiveadminQuillEditor` namespace, mirroring existing files. JavaScript additions in `app/assets/javascripts` should align with the lintable ES module style in `index.js`; run `npx eslint index.js` if you modify the package entry point.
|
||||
|
||||
## Testing Guidelines
|
||||
RSpec powers the suite; place feature-level coverage in `spec/system`, supporting components in `spec/page_objects`, and pure Ruby behaviour in `spec`. Prefer descriptive example names (`it "renders toolbar buttons"`) and keep factories in the dummy app to mirror real usage. Start tests with `make specs`; use `bin/rspec spec/system/editor_spec.rb` to target a file.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
Recent history follows conventional prefixes (`fix:`, `ci:`, `chore:`); keep messages in the imperative mood and limit the subject to 72 characters. Every pull request should link related GitHub issues, summarize user-facing changes, and note any docs or assets touched. Include screenshots or GIFs when altering the editor UI, and confirm that both `make specs` and `make lint` pass in the PR description. Request review from a maintainer and re-run the pipeline after rebases.
|
||||
|
||||
## Security & Configuration Tips
|
||||
Store local-only overrides in `extra/.env` (kept out of version control) and avoid committing credentials to the dummy app. The docker-compose services mount the repository directly, so prefer editing files via your host editor rather than inside the container to prevent permission drift.
|
||||
19
Appraisals
Обычный файл
19
Appraisals
Обычный файл
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Rails 7.2 with ActiveAdmin 4.x beta
|
||||
appraise 'rails-7.2-active-admin-4.x' do
|
||||
gem 'rails', '~> 7.2.0'
|
||||
gem 'activeadmin', '4.0.0.beta20'
|
||||
gem 'sqlite3' # 7.2 can use newer sqlite3
|
||||
gem 'importmap-rails'
|
||||
gem 'propshaft'
|
||||
end
|
||||
|
||||
# Rails 8.0 with ActiveAdmin 4.x beta
|
||||
appraise 'rails-8.0-active-admin-4.x' do
|
||||
gem 'rails', '~> 8.0.0'
|
||||
gem 'activeadmin', '4.0.0.beta20'
|
||||
gem 'sqlite3' # Rails 8 needs latest sqlite3
|
||||
gem 'importmap-rails'
|
||||
gem 'propshaft'
|
||||
end
|
||||
72
CHANGELOG.md
Обычный файл
72
CHANGELOG.md
Обычный файл
@@ -0,0 +1,72 @@
|
||||
# Quill Editor for ActiveAdmin
|
||||
|
||||
An Active Admin plugin to use Quill Rich Text Editor.
|
||||
|
||||
## v2.0.0 - 2025-04-15
|
||||
|
||||
- Update Quill from 1.3.7 to 2.0.3
|
||||
|
||||
## v1.3.0 - 2025-03-18
|
||||
|
||||
- Javascript API: expose Quill editor query functions
|
||||
- ImageUploader: reject the Promise if an url is not provided in the response
|
||||
|
||||
## v1.2.0 - 2024-02-29
|
||||
|
||||
- Update minimum supported Ruby version to 3.0
|
||||
|
||||
## v1.1.0 - 2023-08-20
|
||||
|
||||
- Support ActiveAdmin 3.x
|
||||
- Minor tests config improvements
|
||||
|
||||
## v1.0.0 - 2022-04-18
|
||||
|
||||
- Set minimum Ruby version to 2.6.0
|
||||
- Remove `sassc` dependency
|
||||
- Enable Ruby 3.0 specs support
|
||||
- Enable Rails 7.0 specs support
|
||||
- Internal improvements
|
||||
|
||||
## v0.3.4 - 2021-03-16
|
||||
|
||||
- Fix editor loading with Turbolinks
|
||||
|
||||
## v0.3.2 - 2021-03-14
|
||||
|
||||
- Specs improvements
|
||||
- Minor internal changes
|
||||
|
||||
## v0.3.0 - 2020-10-01
|
||||
|
||||
- Add Webpacker support
|
||||
|
||||
## v0.2.14 - 2020-09-10
|
||||
|
||||
- JS: enable strict directive
|
||||
- Minor specs improvement
|
||||
|
||||
## v0.2.12 - 2020-09-08
|
||||
|
||||
- JS refactoring
|
||||
|
||||
## v0.2.10 - 2020-09-05
|
||||
|
||||
- Include Image Uploader plugin
|
||||
|
||||
## v0.2.9 - 2020-09-04
|
||||
|
||||
- Fix not working alignments (issue #8)
|
||||
- Fix empty editor problem which produces `<p><br></p>` (pull request #9)
|
||||
|
||||
## v0.2.8 - 2020-09-03
|
||||
|
||||
- Minor style improvements
|
||||
- Add specs for editor in nested resources
|
||||
- Add Rubocop and remove SimpleCov gems
|
||||
|
||||
## v0.2.4 - 2020-09-01
|
||||
|
||||
- Update Quill editor to version 1.3.7
|
||||
- Add minimum specs (using RSpec)
|
||||
- Minor internal changes
|
||||
90
Gemfile
90
Gemfile
@@ -1,4 +1,92 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gemspec
|
||||
def eval_version(dependency, version)
|
||||
return [dependency] if version.empty?
|
||||
|
||||
# Handle versions that already have operators like ~>, >=, etc.
|
||||
if version.match?(/^[~<>=]/)
|
||||
[dependency, version]
|
||||
else
|
||||
# Add ~> operator and ensure version has at least 2 dots
|
||||
version.count('.') < 2 ? [dependency, "~> #{version}.0"] : [dependency, "~> #{version}"]
|
||||
end
|
||||
end
|
||||
|
||||
if ENV['DEVEL'] == '1'
|
||||
gem 'activeadmin_quill_editor', path: './'
|
||||
else
|
||||
gemspec
|
||||
end
|
||||
|
||||
ruby_ver = ENV.fetch('RUBY_VERSION', '')
|
||||
|
||||
rails_ver = ENV.fetch('RAILS_VERSION', '')
|
||||
rails = eval_version('rails', rails_ver)
|
||||
gem(*rails)
|
||||
|
||||
active_admin_ver = ENV.fetch('ACTIVEADMIN_VERSION', '')
|
||||
active_admin_requirement =
|
||||
if active_admin_ver.empty?
|
||||
'~> 4.0.0.beta16'
|
||||
elsif active_admin_ver.match?(/[a-zA-Z]/) || active_admin_ver.match?(/^[~<>=]/)
|
||||
active_admin_ver
|
||||
else
|
||||
target = Gem::Version.new(active_admin_ver)
|
||||
if target >= Gem::Version.new('4.0')
|
||||
'~> 4.0.0.beta16'
|
||||
else
|
||||
active_admin_ver.count('.') < 2 ? "~> #{active_admin_ver}.0" : "~> #{active_admin_ver}"
|
||||
end
|
||||
end
|
||||
|
||||
gem 'activeadmin', active_admin_requirement
|
||||
|
||||
ruby32 = ruby_ver.empty? || Gem::Version.new(ruby_ver) >= Gem::Version.new('3.2')
|
||||
rails72 = rails_ver.empty? || Gem::Version.new(rails_ver) >= Gem::Version.new('7.2')
|
||||
sqlite3 = ruby32 && rails72 ? ['sqlite3'] : ['sqlite3', '~> 1.4']
|
||||
gem(*sqlite3)
|
||||
|
||||
gem 'zeitwerk', '~> 2.6.18' unless ruby32
|
||||
|
||||
# NOTE: to avoid error: uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger
|
||||
gem 'concurrent-ruby', '1.3.4'
|
||||
|
||||
# Misc
|
||||
gem 'bigdecimal'
|
||||
gem 'csv'
|
||||
gem 'mutex_m'
|
||||
gem 'puma'
|
||||
|
||||
# Asset pipeline - use Propshaft for Rails 8, Sprockets for older versions
|
||||
rails80 = rails_ver.empty? || Gem::Version.new(rails_ver) >= Gem::Version.new('8.0')
|
||||
if rails80
|
||||
gem 'importmap-rails' # Required for ActiveAdmin 4
|
||||
gem 'propshaft'
|
||||
else
|
||||
gem 'sassc'
|
||||
gem 'sprockets-rails'
|
||||
end
|
||||
|
||||
# Testing
|
||||
gem 'capybara'
|
||||
gem 'cuprite'
|
||||
gem 'rspec_junit_formatter'
|
||||
gem 'rspec-rails'
|
||||
gem 'simplecov', require: false
|
||||
gem 'super_diff'
|
||||
|
||||
# Linters
|
||||
gem 'fasterer'
|
||||
gem 'rubocop'
|
||||
gem 'rubocop-capybara'
|
||||
gem 'rubocop-packaging'
|
||||
gem 'rubocop-performance'
|
||||
gem 'rubocop-rails'
|
||||
gem 'rubocop-rspec'
|
||||
gem 'rubocop-rspec_rails'
|
||||
|
||||
# Tools
|
||||
gem 'appraisal'
|
||||
gem 'pry-rails'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017 Mattia Roccoberton
|
||||
Copyright (c) 2017-2022 Mattia Roccoberton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
47
Makefile
Обычный файл
47
Makefile
Обычный файл
@@ -0,0 +1,47 @@
|
||||
include extra/.env
|
||||
|
||||
help:
|
||||
@echo -e "${COMPOSE_PROJECT_NAME} - Main project commands:\n\
|
||||
make up # starts the dev services (optional env vars: RUBY / RAILS / ACTIVEADMIN)\n\
|
||||
make specs # run the tests (after up)\n\
|
||||
make lint # run the linters (after up)\n\
|
||||
make server # run the server (after up)\n\
|
||||
make shell # open a shell (after up)\n\
|
||||
make down # cleanup (after up)\n\
|
||||
Example: RUBY=3.2 RAILS=7.1 ACTIVEADMIN=3.2.0 make up"
|
||||
|
||||
# System commands
|
||||
|
||||
build:
|
||||
@rm -f Gemfile.lock spec/dummy/db/*.sqlite3
|
||||
@docker compose -f extra/docker-compose.yml build
|
||||
|
||||
db_reset:
|
||||
@docker compose -f extra/docker-compose.yml run --rm app bin/rails db:create db:migrate db:test:prepare
|
||||
|
||||
up: build db_reset
|
||||
@docker compose -f extra/docker-compose.yml up
|
||||
|
||||
shell:
|
||||
@docker compose -f extra/docker-compose.yml exec app bash
|
||||
|
||||
down:
|
||||
@docker compose -f extra/docker-compose.yml down --volumes --rmi local --remove-orphans
|
||||
|
||||
# App commands
|
||||
|
||||
seed:
|
||||
@docker compose -f extra/docker-compose.yml exec app bin/rails db:seed
|
||||
|
||||
console: seed
|
||||
@docker compose -f extra/docker-compose.yml exec app bin/rails console
|
||||
|
||||
lint:
|
||||
@docker compose -f extra/docker-compose.yml exec app bin/rubocop
|
||||
|
||||
server: seed
|
||||
@rm -f spec/dummy/tmp/pids/server.pid
|
||||
@docker compose -f extra/docker-compose.yml exec app bin/rails server -b 0.0.0.0 -p ${SERVER_PORT}
|
||||
|
||||
specs:
|
||||
@docker compose -f extra/docker-compose.yml exec app bin/rspec --fail-fast
|
||||
105
README.md
105
README.md
@@ -1,33 +1,59 @@
|
||||
# ActiveAdmin Quill Editor [](https://badge.fury.io/rb/activeadmin_quill_editor)
|
||||
# ActiveAdmin Quill Editor
|
||||
[](https://badge.fury.io/rb/activeadmin_quill_editor)
|
||||
[](https://rubygems.org/gems/activeadmin_quill_editor)
|
||||
[](https://github.com/blocknotes/activeadmin_quill_editor/actions/workflows/ci.yml)
|
||||
|
||||
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.
|
||||
|
||||

|
||||

|
||||
|
||||
Please :star: if you like it.
|
||||
|
||||
## Install
|
||||
|
||||
- Update your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
|
||||
- Add at the end of your ActiveAdmin styles (_app/assets/stylesheets/active_admin.scss_):
|
||||
```css
|
||||
_NOTE_: the gem 2.x provide Quill 2.x, while Quill 1.x is provided with the gem version 1.x
|
||||
|
||||
After installing Active Admin, add to your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
|
||||
|
||||
If you installed Active Admin without Webpacker support (default for now):
|
||||
|
||||
- Add a SASS/SCSS gem to your Gemfile (ex. `gem 'sassc'`)
|
||||
- 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.
|
||||
If you installed Active Admin with Webpacker support:
|
||||
|
||||
## Options
|
||||
- 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')
|
||||
```
|
||||
|
||||
**data-options**: permits to set *quill editor* options directly - see [options list](https://quilljs.com/docs/quickstart/)
|
||||
## 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/)
|
||||
|
||||
If you are using Action Text (`has_rich_text`) on the same field of the Quill editor please take a look at [this workaround](https://github.com/blocknotes/activeadmin_quill_editor/issues/33#issuecomment-1965996947) to avoid issues.
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic usage
|
||||
|
||||
```ruby
|
||||
# ActiveAdmin article form conf:
|
||||
# Active Admin article form conf:
|
||||
form do |f|
|
||||
f.inputs 'Article' do
|
||||
f.input :title
|
||||
@@ -38,26 +64,59 @@ 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
|
||||
|
||||
- Upload functions (Images, Documents, Files, etc.) are not implemented yet
|
||||
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).
|
||||
|
||||
```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).
|
||||
|
||||
## Javascript API
|
||||
|
||||
Some methods are provided for advanced use cases:
|
||||
|
||||
- `window.getQuillEditors()`: returns all the available Quill editors instances;
|
||||
- `window.getQuillEditorByIndex(n)`: returns the N-th Quill editor instance;
|
||||
- `window.getQuillEditorByElementId(id)`: returns the Quill editor instance related to the specified element id (e.g. _article_description_).
|
||||
|
||||
## Development
|
||||
|
||||
Project created by [Mattia Roccoberton](http://blocknot.es), thanks also to the good guys that opened issues and pull requests from time to time.
|
||||
|
||||
For development information please check [this document](extra/development.md).
|
||||
|
||||
## 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.
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
## Contributors
|
||||
|
||||
- [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
|
||||
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE.txt)
|
||||
The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
|
||||
|
||||
28
Rakefile
28
Rakefile
@@ -1,3 +1,27 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/gem_tasks"
|
||||
begin
|
||||
require 'bundler/setup'
|
||||
rescue LoadError
|
||||
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||
end
|
||||
|
||||
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
||||
load 'rails/tasks/engine.rake'
|
||||
|
||||
load 'rails/tasks/statistics.rake'
|
||||
|
||||
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
|
||||
|
||||
30
activeadmin-quill_editor.gemspec
Обычный файл
30
activeadmin-quill_editor.gemspec
Обычный файл
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
lib = File.expand_path('lib', __dir__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'activeadmin/quill_editor/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'activeadmin-quill_editor'
|
||||
spec.version = ActiveAdmin::QuillEditor::VERSION
|
||||
spec.summary = 'Quill Editor for ActiveAdmin'
|
||||
spec.description = 'An Active Admin plugin to use Quill Rich Text Editor'
|
||||
spec.license = 'MIT'
|
||||
spec.authors = ['Mattia Roccoberton']
|
||||
spec.email = 'mat@blocknot.es'
|
||||
spec.homepage = 'https://github.com/rs-pro/activeadmin-quill_editor'
|
||||
|
||||
spec.required_ruby_version = '>= 3.2'
|
||||
|
||||
spec.metadata['homepage_uri'] = spec.homepage
|
||||
spec.metadata['changelog_uri'] = 'https://github.com/rs-pro/activeadmin-quill_editor/blob/main/CHANGELOG.md'
|
||||
spec.metadata['source_code_uri'] = spec.homepage
|
||||
|
||||
spec.metadata['rubygems_mfa_required'] = 'true'
|
||||
|
||||
spec.files = Dir['{app,lib,vendor}/**/*', 'LICENSE.txt', 'Rakefile', 'README.md']
|
||||
spec.require_paths = ['lib']
|
||||
|
||||
spec.add_dependency 'activeadmin', '>= 4.0.0.beta', '< 5'
|
||||
spec.add_dependency 'rails', '>= 7.2'
|
||||
end
|
||||
@@ -1,19 +0,0 @@
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'activeadmin/quill_editor/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'activeadmin_quill_editor'
|
||||
spec.version = ActiveAdmin::QuillEditor::VERSION
|
||||
spec.summary = 'Quill Editor for ActiveAdmin'
|
||||
spec.description = 'An Active Admin plugin to use Quill Rich Text Editor'
|
||||
spec.license = 'MIT'
|
||||
spec.authors = ['Mattia Roccoberton']
|
||||
spec.email = 'mat@blocknot.es'
|
||||
spec.homepage = 'https://github.com/blocknotes/activeadmin_quill_editor'
|
||||
|
||||
spec.files = `git ls-files -z`.split("\x0")
|
||||
spec.require_paths = ['lib']
|
||||
|
||||
spec.add_runtime_dependency 'activeadmin', '~> 1.0'
|
||||
end
|
||||
362
app/assets/javascripts/activeadmin/quill.imageUploader.js
Обычный файл
362
app/assets/javascripts/activeadmin/quill.imageUploader.js
Обычный файл
@@ -0,0 +1,362 @@
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony import */ var _blots_image_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
||||
/* harmony import */ var _quill_imageUploader_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
||||
/* harmony import */ var _quill_imageUploader_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_quill_imageUploader_css__WEBPACK_IMPORTED_MODULE_1__);
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
|
||||
|
||||
|
||||
var ImageUploader = function () {
|
||||
function ImageUploader(quill, options) {
|
||||
_classCallCheck(this, ImageUploader);
|
||||
|
||||
this.quill = quill;
|
||||
this.options = options;
|
||||
this.range = null;
|
||||
|
||||
if (typeof this.options.upload !== "function") {
|
||||
console.warn("[Missing config] upload function that returns a promise is required");
|
||||
}
|
||||
|
||||
if (this.options.loadingClass) {
|
||||
_blots_image_js__WEBPACK_IMPORTED_MODULE_0__["default"].className = this.options.loadingClass;
|
||||
}
|
||||
|
||||
var toolbar = this.quill.getModule("toolbar");
|
||||
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, false);
|
||||
this.quill.root.addEventListener("paste", this.handlePaste, false);
|
||||
}
|
||||
|
||||
_createClass(ImageUploader, [{
|
||||
key: "selectLocalImage",
|
||||
value: function selectLocalImage() {
|
||||
var _this = 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(_this.fileHolder);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "handleDrop",
|
||||
value: function handleDrop(evt) {
|
||||
var _this2 = this;
|
||||
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
if (evt.dataTransfer && evt.dataTransfer.files && evt.dataTransfer.files.length) {
|
||||
if (document.caretRangeFromPoint) {
|
||||
var selection = document.getSelection();
|
||||
var range = document.caretRangeFromPoint(evt.clientX, evt.clientY);
|
||||
if (selection && range) {
|
||||
selection.setBaseAndExtent(range.startContainer, range.startOffset, range.startContainer, range.startOffset);
|
||||
}
|
||||
} else {
|
||||
var _selection = document.getSelection();
|
||||
var _range = document.caretPositionFromPoint(evt.clientX, evt.clientY);
|
||||
if (_selection && _range) {
|
||||
_selection.setBaseAndExtent(_range.offsetNode, _range.offset, _range.offsetNode, _range.offset);
|
||||
}
|
||||
}
|
||||
|
||||
this.range = this.quill.getSelection();
|
||||
var file = evt.dataTransfer.files[0];
|
||||
|
||||
setTimeout(function () {
|
||||
_this2.range = _this2.quill.getSelection();
|
||||
_this2.readAndUploadFile(file);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "handlePaste",
|
||||
value: function handlePaste(evt) {
|
||||
var _this3 = this;
|
||||
|
||||
var clipboard = evt.clipboardData || window.clipboardData;
|
||||
|
||||
// IE 11 is .files other browsers are .items
|
||||
if (clipboard && (clipboard.items || clipboard.files)) {
|
||||
var items = clipboard.items || clipboard.files;
|
||||
var IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (IMAGE_MIME_REGEX.test(items[i].type)) {
|
||||
(function () {
|
||||
var file = items[i].getAsFile ? items[i].getAsFile() : items[i];
|
||||
|
||||
if (file) {
|
||||
_this3.range = _this3.quill.getSelection();
|
||||
evt.preventDefault();
|
||||
setTimeout(function () {
|
||||
_this3.range = _this3.quill.getSelection();
|
||||
_this3.readAndUploadFile(file);
|
||||
}, 0);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "readAndUploadFile",
|
||||
value: function readAndUploadFile(file) {
|
||||
var _this4 = this;
|
||||
|
||||
var isUploadReject = false;
|
||||
|
||||
var fileReader = new FileReader();
|
||||
|
||||
fileReader.addEventListener("load", function () {
|
||||
if (!isUploadReject) {
|
||||
var base64ImageSrc = fileReader.result;
|
||||
_this4.insertBase64Image(base64ImageSrc);
|
||||
}
|
||||
}, false);
|
||||
|
||||
if (file) {
|
||||
fileReader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
this.options.upload(file).then(function (imageUrl) {
|
||||
_this4.insertToEditor(imageUrl);
|
||||
}, function (error) {
|
||||
isUploadReject = true;
|
||||
_this4.removeBase64Image();
|
||||
console.warn(error);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "fileChanged",
|
||||
value: function fileChanged() {
|
||||
var file = this.fileHolder.files[0];
|
||||
this.readAndUploadFile(file);
|
||||
}
|
||||
}, {
|
||||
key: "insertBase64Image",
|
||||
value: function insertBase64Image(url) {
|
||||
var range = this.range;
|
||||
|
||||
this.quill.insertEmbed(range.index, _blots_image_js__WEBPACK_IMPORTED_MODULE_0__["default"].blotName, "" + url, "user");
|
||||
}
|
||||
}, {
|
||||
key: "insertToEditor",
|
||||
value: function insertToEditor(url) {
|
||||
var range = this.range;
|
||||
// Delete the placeholder image
|
||||
|
||||
this.quill.deleteText(range.index, 3, "user");
|
||||
// Insert the server saved image
|
||||
this.quill.insertEmbed(range.index, "image", "" + url, "user");
|
||||
|
||||
range.index++;
|
||||
this.quill.setSelection(range, "user");
|
||||
}
|
||||
}, {
|
||||
key: "removeBase64Image",
|
||||
value: function removeBase64Image() {
|
||||
var range = this.range;
|
||||
|
||||
this.quill.deleteText(range.index, 3, "user");
|
||||
}
|
||||
}]);
|
||||
|
||||
return ImageUploader;
|
||||
}();
|
||||
|
||||
window.ImageUploader = ImageUploader;
|
||||
/* harmony default export */ __webpack_exports__["default"] = (ImageUploader);
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony import */ var quill__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
||||
/* harmony import */ var quill__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(quill__WEBPACK_IMPORTED_MODULE_0__);
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
|
||||
|
||||
var InlineBlot = quill__WEBPACK_IMPORTED_MODULE_0___default.a.import('blots/block');
|
||||
|
||||
var LoadingImage = function (_InlineBlot) {
|
||||
_inherits(LoadingImage, _InlineBlot);
|
||||
|
||||
function LoadingImage() {
|
||||
_classCallCheck(this, LoadingImage);
|
||||
|
||||
return _possibleConstructorReturn(this, (LoadingImage.__proto__ || Object.getPrototypeOf(LoadingImage)).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(LoadingImage, [{
|
||||
key: 'deleteAt',
|
||||
value: function deleteAt(index, length) {
|
||||
_get(LoadingImage.prototype.__proto__ || Object.getPrototypeOf(LoadingImage.prototype), 'deleteAt', this).call(this, index, length);
|
||||
this.cache = {};
|
||||
}
|
||||
}], [{
|
||||
key: 'create',
|
||||
value: function create(src) {
|
||||
var node = _get(LoadingImage.__proto__ || Object.getPrototypeOf(LoadingImage), 'create', this).call(this, src);
|
||||
if (src === true) return node;
|
||||
|
||||
var image = document.createElement('img');
|
||||
image.setAttribute('src', src);
|
||||
node.appendChild(image);
|
||||
return node;
|
||||
}
|
||||
}, {
|
||||
key: 'value',
|
||||
value: function value(domNode) {
|
||||
var _domNode$dataset = domNode.dataset,
|
||||
src = _domNode$dataset.src,
|
||||
custom = _domNode$dataset.custom;
|
||||
|
||||
return { src: src, custom: custom };
|
||||
}
|
||||
}]);
|
||||
|
||||
return LoadingImage;
|
||||
}(InlineBlot);
|
||||
|
||||
LoadingImage.blotName = 'imageBlot';
|
||||
LoadingImage.className = 'quill-image-uploading';
|
||||
LoadingImage.tagName = 'span';
|
||||
quill__WEBPACK_IMPORTED_MODULE_0___default.a.register({ 'formats/imageBlot': LoadingImage });
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = (LoadingImage);
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = Quill;
|
||||
|
||||
/***/ }),
|
||||
/* 3 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
// removed by extract-text-webpack-plugin
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
||||
1
app/assets/javascripts/activeadmin/quill.imageUploader.min.js
поставляемый
Обычный файл
1
app/assets/javascripts/activeadmin/quill.imageUploader.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
8
app/assets/javascripts/activeadmin/quill_editor/quill.min.js
поставляемый
8
app/assets/javascripts/activeadmin/quill_editor/quill.min.js
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -1,39 +0,0 @@
|
||||
window.onload = function() {
|
||||
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'],
|
||||
// [{ 'size': ['small', false, 'large', 'huge'] }],
|
||||
// [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
||||
]
|
||||
},
|
||||
placeholder: '',
|
||||
theme: 'snow'
|
||||
};
|
||||
|
||||
for( var i = 0; i < editors.length; i++ ) {
|
||||
var content = editors[i].querySelector( '.quill-editor-content' );
|
||||
if( content ) {
|
||||
var options = editors[0].getAttribute( 'data-options' ) ? JSON.parse( editors[0].getAttribute( 'data-options' ) ) : default_options;
|
||||
editors[i]['_quill-editor'] = new Quill( content, options );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// input.value = editors[i]['_quill-editor'].getContents();
|
||||
// input.value = editors[i].querySelector( '.quill-editor-content' ).innerHTML;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
@import 'activeadmin/quill_editor/quill.snow'
|
||||
|
||||
body.active_admin form .quill-editor
|
||||
display: inline-block
|
||||
width: calc(80% - 22px)
|
||||
.ql-editor
|
||||
background-color: #fff
|
||||
max-height: 300px
|
||||
min-height: 150px
|
||||
padding: 10px
|
||||
// reset internal elements
|
||||
*
|
||||
margin: initial
|
||||
padding: initial
|
||||
text-align: initial
|
||||
p
|
||||
margin-bottom: 1em
|
||||
ol
|
||||
list-style-type: decimal
|
||||
ul
|
||||
list-style-type: disc
|
||||
ul, ol
|
||||
margin: 0 1.5em 1.5em 0
|
||||
padding-left: 1.5em
|
||||
56
app/assets/stylesheets/activeadmin/_quill_editor_input.scss
Обычный файл
56
app/assets/stylesheets/activeadmin/_quill_editor_input.scss
Обычный файл
@@ -0,0 +1,56 @@
|
||||
// reset internal elements
|
||||
.ql-editor * {
|
||||
margin: initial;
|
||||
padding: initial;
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
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 {
|
||||
background-color: #fff;
|
||||
max-height: 300px;
|
||||
min-height: 150px;
|
||||
padding: 10px;
|
||||
word-break: break-all;
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 0 1.5em 1.5em 0;
|
||||
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 @@
|
||||
.quill-image-uploading {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.quill-image-uploading img {
|
||||
max-width: 98% !important;
|
||||
filter: blur(5px);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.quill-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: #1e40af;
|
||||
z-index: 1;
|
||||
animation: spinner 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spinner {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
29
bin/fasterer
Исполняемый файл
29
bin/fasterer
Исполняемый файл
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'fasterer' 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("fasterer", "fasterer")
|
||||
16
bin/rails
Исполняемый файл
16
bin/rails
Исполняемый файл
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env ruby
|
||||
# This command will automatically be run when you run "rails" with Rails gems
|
||||
# installed from the root of your application.
|
||||
|
||||
ENV['RAILS_ENV'] ||= 'development'
|
||||
|
||||
ENGINE_ROOT = File.expand_path('..', __dir__)
|
||||
ENGINE_PATH = File.expand_path('../lib/activeadmin/quill_editor/engine', __dir__)
|
||||
APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__)
|
||||
|
||||
# Set up gems listed in the Gemfile.
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
||||
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
||||
|
||||
require 'rails/all'
|
||||
require 'rails/engine/commands'
|
||||
29
bin/rake
Исполняемый файл
29
bin/rake
Исполняемый файл
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rake' 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("rake", "rake")
|
||||
29
bin/rspec
Исполняемый файл
29
bin/rspec
Исполняемый файл
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rspec' 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("rspec-core", "rspec")
|
||||
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")
|
||||
169
docs/activeadmin-4-asset-setup.md
Обычный файл
169
docs/activeadmin-4-asset-setup.md
Обычный файл
@@ -0,0 +1,169 @@
|
||||
# ActiveAdmin 4 Asset Setup (Tailwind CSS v4)
|
||||
|
||||
## Overview
|
||||
This guide documents the current, working setup for ActiveAdmin 4.0.0.beta20 with Tailwind CSS v4 and Quill Editor.
|
||||
|
||||
## Requirements
|
||||
- ActiveAdmin 4.0.0.beta20
|
||||
- Tailwind CSS v4 via `tailwindcss-rails` (ships `bin/tailwindcss`)
|
||||
- ESBuild (or other JS bundler) for ActiveAdmin JS + Quill Editor
|
||||
|
||||
## 1. Gemfile
|
||||
|
||||
```ruby
|
||||
# ActiveAdmin 4 beta
|
||||
gem "activeadmin", "4.0.0.beta20"
|
||||
|
||||
gem "activeadmin_quill_editor"
|
||||
|
||||
# Tailwind CSS v4 (bundled CLI)
|
||||
gem "tailwindcss-rails", "~> 4.4.0"
|
||||
```
|
||||
|
||||
## 2. package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@activeadmin/activeadmin": "^4.0.0-beta20",
|
||||
"quill": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.27.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build:js": "node esbuild-active_admin.config.js",
|
||||
"build:css": "bundle exec rake active_admin:build",
|
||||
"build": "npm run build:js && npm run build:css",
|
||||
"watch:js": "node esbuild-active_admin.config.js --watch",
|
||||
"watch:css": "bundle exec rake active_admin:watch",
|
||||
"dev": "npm run watch:js & npm run watch:css"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. JavaScript Entry (app/javascript/active_admin.js)
|
||||
|
||||
```javascript
|
||||
import "@activeadmin/activeadmin";
|
||||
|
||||
import Quill from "quill";
|
||||
window.Quill = Quill;
|
||||
|
||||
import "activeadmin_quill_editor";
|
||||
```
|
||||
|
||||
## 4. Tailwind Input (app/assets/stylesheets/active_admin.tailwind.css)
|
||||
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
@config "../../../tailwind-active_admin.config.mjs";
|
||||
|
||||
@import "activeadmin/quill_editor/quill.snow";
|
||||
@import "activeadmin/quill_editor_input";
|
||||
```
|
||||
|
||||
## 5. Tailwind Config (tailwind-active_admin.config.mjs)
|
||||
|
||||
```javascript
|
||||
import { execSync } from "child_process";
|
||||
import activeAdminPlugin from "@activeadmin/activeadmin/plugin";
|
||||
|
||||
const activeAdminPath = execSync("bundle show activeadmin", {
|
||||
encoding: "utf-8"
|
||||
}).trim();
|
||||
|
||||
export default {
|
||||
content: [
|
||||
`${activeAdminPath}/vendor/javascript/flowbite.js`,
|
||||
`${activeAdminPath}/plugin.js`,
|
||||
`${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`,
|
||||
"./app/admin/**/*.{arb,erb,html,rb}`,
|
||||
"./app/views/active_admin/**/*.{arb,erb,html,rb}`,
|
||||
"./app/views/admin/**/*.{arb,erb,html,rb}`,
|
||||
"./app/views/layouts/active_admin*.{erb,html}`,
|
||||
"./app/javascript/**/*.js"
|
||||
],
|
||||
darkMode: "selector",
|
||||
plugins: [activeAdminPlugin]
|
||||
};
|
||||
```
|
||||
|
||||
## 6. Tailwind Build Tasks (lib/tasks/active_admin.rake)
|
||||
|
||||
```ruby
|
||||
namespace :active_admin do
|
||||
desc "Build Active Admin Tailwind stylesheets"
|
||||
task build: :environment do
|
||||
command = [
|
||||
Rails.root.join("bin/tailwindcss").to_s,
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
|
||||
desc "Watch Active Admin Tailwind stylesheets"
|
||||
task watch: :environment do
|
||||
command = [
|
||||
Rails.root.join("bin/tailwindcss").to_s,
|
||||
"--watch",
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
end
|
||||
|
||||
Rake::Task["assets:precompile"].enhance(["active_admin:build"])
|
||||
Rake::Task["test:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("test:prepare")
|
||||
Rake::Task["spec:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("spec:prepare")
|
||||
Rake::Task["db:test:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("db:test:prepare")
|
||||
```
|
||||
|
||||
## 7. Tailwind CLI Binstub
|
||||
|
||||
```bash
|
||||
bundle binstubs tailwindcss-ruby --force
|
||||
```
|
||||
|
||||
## 8. Upgrade Notes (ActiveAdmin 4.0.0.beta20)
|
||||
|
||||
From ActiveAdmin's upgrade guide:
|
||||
- `_site_header.html.erb` container class changed from `sticky` to `fixed`.
|
||||
- `active_admin.html.erb` adds the `pt-16` utility class.
|
||||
- Tailwind v4 uses `@import "tailwindcss"` and `@config` (no `@tailwind` directives).
|
||||
- The Tailwind config file is ESM. Use `tailwind-active_admin.config.mjs`.
|
||||
- jQuery/jQuery UI removed; `columns` and `tabs` components removed.
|
||||
- Replace `default_main_content` with `render "show_default"`.
|
||||
- Replace `as: :datepicker` with `as: :date_picker`.
|
||||
- Replace `active_admin_comments` with `active_admin_comments_for(resource)`.
|
||||
- Replace `attributes_table` with `attributes_table_for(resource)` in sidebars.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### CSS not building
|
||||
- Ensure `tailwindcss-rails` is pinned to `~> 4.4.0`.
|
||||
- Run `bundle binstubs tailwindcss-ruby --force`.
|
||||
- Confirm `bin/tailwindcss` exists.
|
||||
|
||||
### ActiveAdmin styles not loading
|
||||
- Verify `app/assets/builds/active_admin.css` exists.
|
||||
- Check that `active_admin.tailwind.css` imports Quill Editor styles.
|
||||
|
||||
### Quill Editor not initializing
|
||||
- Verify `window.Quill` in the console.
|
||||
- Ensure `activeadmin_quill_editor` is imported.
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Update ActiveAdmin to 4.0.0.beta20
|
||||
- [ ] Use Tailwind v4 `@import` + `@config`
|
||||
- [ ] Add `tailwindcss-rails` (~> 4.4.0) and binstub
|
||||
- [ ] Create `tailwind-active_admin.config.mjs` (ESM)
|
||||
- [ ] Update rake tasks to use `bin/tailwindcss` (no `-c`)
|
||||
- [ ] Rebuild assets and verify UI
|
||||
371
docs/activeadmin-4-changelog.md
Обычный файл
371
docs/activeadmin-4-changelog.md
Обычный файл
@@ -0,0 +1,371 @@
|
||||
# ActiveAdmin v4.0.0 Beta Changelog
|
||||
|
||||
This document contains all the changes from ActiveAdmin v4.0.0 beta releases.
|
||||
|
||||
**Source**: [https://github.com/activeadmin/activeadmin/releases](https://github.com/activeadmin/activeadmin/releases)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta20 (2025-01-27)
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Drop support for Rails 7.0 and 7.1 [#8908](https://github.com/activeadmin/activeadmin/pull/8908)
|
||||
|
||||
### Template Updates 📝
|
||||
- Link to parent menu item [#8225](https://github.com/activeadmin/activeadmin/pull/8225)
|
||||
|
||||
### Enhancements ✨
|
||||
- resource: allow resource_class to be passed as a String [#8848](https://github.com/activeadmin/activeadmin/pull/8848)
|
||||
- Remove horizontal padding on comments pagination [#8925](https://github.com/activeadmin/activeadmin/pull/8925)
|
||||
- Add page anchor to comments form view [#8926](https://github.com/activeadmin/activeadmin/pull/8926)
|
||||
- Fix vertical spacing on has-many forms [#8927](https://github.com/activeadmin/activeadmin/pull/8927)
|
||||
- Remove no-wrap from table columns [#8928](https://github.com/activeadmin/activeadmin/pull/8928)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Restore pointer cursor on eligible buttons [#8878](https://github.com/activeadmin/activeadmin/pull/8878)
|
||||
- Ignore extra bundler output when DEBUG is set [#8895](https://github.com/activeadmin/activeadmin/pull/8895)
|
||||
|
||||
### Other Changes 🛠
|
||||
- Test supported Rails versions against Ruby 4.0 [#8898](https://github.com/activeadmin/activeadmin/pull/8898)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta19...v4.0.0.beta20](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta19...v4.0.0.beta20)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta19 (2024-11-20)
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Bump tailwind from 3.4.18 to 4.1.17 [#8709](https://github.com/activeadmin/activeadmin/pull/8709)
|
||||
|
||||
### Template Updates 📝
|
||||
- Bump tailwind from 3.4.18 to 4.1.17 [#8709](https://github.com/activeadmin/activeadmin/pull/8709)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Remove self-reference to ActiveAdmin node package [#8847](https://github.com/activeadmin/activeadmin/pull/8847)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta18...v4.0.0.beta19](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta18...v4.0.0.beta19)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta18 (2024-11-01)
|
||||
|
||||
### Enhancements ✨
|
||||
- Add Rails 8.1.0 compatibility [#8837](https://github.com/activeadmin/activeadmin/pull/8837)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta17...v4.0.0.beta18](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta17...v4.0.0.beta18)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta17 (2024-10-21)
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Preserve custom string labels in AttributesTable headers [#8815](https://github.com/activeadmin/activeadmin/pull/8815)
|
||||
|
||||
### Enhancements ✨
|
||||
- Update French locales [#8770](https://github.com/activeadmin/activeadmin/pull/8770)
|
||||
- Improve text color contrast [#8535](https://github.com/activeadmin/activeadmin/pull/8535)
|
||||
- Remove unnecessary base CSS styles [#8803](https://github.com/activeadmin/activeadmin/pull/8803)
|
||||
- Update dark mode CSS styles [#8809](https://github.com/activeadmin/activeadmin/pull/8809)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta16...v4.0.0.beta17](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta16...v4.0.0.beta17)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta16 (2024-07-23)
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Drop Ruby 3.1 and update dependencies [#8760](https://github.com/activeadmin/activeadmin/pull/8760)
|
||||
- Remove Tabs component [#8762](https://github.com/activeadmin/activeadmin/pull/8762)
|
||||
|
||||
### Template Updates 📝
|
||||
- Remove `frozen_string_literal` comment from `.arb` templates [#8600](https://github.com/activeadmin/activeadmin/pull/8600)
|
||||
|
||||
### Enhancements ✨
|
||||
- Bump Formtastic to 5.0 and remove legacy code [#8613](https://github.com/activeadmin/activeadmin/pull/8613)
|
||||
- Add Polish pagination entries translations [#8636](https://github.com/activeadmin/activeadmin/pull/8636)
|
||||
- Support sortable argument in id_column [#8639](https://github.com/activeadmin/activeadmin/pull/8639)
|
||||
- Support title for id_column [#8641](https://github.com/activeadmin/activeadmin/pull/8641)
|
||||
- Update pt-BR translations [#8679](https://github.com/activeadmin/activeadmin/pull/8679)
|
||||
- Upgrade Flowbite dependency to v3.1.2 [#8692](https://github.com/activeadmin/activeadmin/pull/8692)
|
||||
- Add some :uk locales [#8732](https://github.com/activeadmin/activeadmin/pull/8732)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Fix deprecation warning in Ruby 3.4 [#8593](https://github.com/activeadmin/activeadmin/pull/8593)
|
||||
- Fix circular require warning in `belongs_to.rb` [#8599](https://github.com/activeadmin/activeadmin/pull/8599)
|
||||
- Fix pagination truncate translation key [#8678](https://github.com/activeadmin/activeadmin/pull/8678)
|
||||
- Fix typo in batch_actions locale translation key [#8712](https://github.com/activeadmin/activeadmin/pull/8712)
|
||||
- Enhance main navbar to improve bouncing on macOS [#8727](https://github.com/activeadmin/activeadmin/pull/8727)
|
||||
- Remove double bottom border on last table row [#8761](https://github.com/activeadmin/activeadmin/pull/8761)
|
||||
|
||||
### Other Changes 🛠
|
||||
- Normalize i18n locale files [#8715](https://github.com/activeadmin/activeadmin/pull/8715)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta15...v4.0.0.beta16](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta15...v4.0.0.beta16)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta15 (2024-12-07)
|
||||
|
||||
### Template Updates 📝
|
||||
- Use ESM in tailwind config template [#8568](https://github.com/activeadmin/activeadmin/pull/8568)
|
||||
|
||||
### Enhancements ✨
|
||||
- Update Korean locale with new translations [#8554](https://github.com/activeadmin/activeadmin/pull/8554)
|
||||
- Update Arabic locale translations [#8555](https://github.com/activeadmin/activeadmin/pull/8555)
|
||||
- Fix an issue with Arabic translation [#8557](https://github.com/activeadmin/activeadmin/pull/8557)
|
||||
- Update Russian locale with new translations [#8558](https://github.com/activeadmin/activeadmin/pull/8558)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta14...v4.0.0.beta15](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta14...v4.0.0.beta15)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta14 (2024-11-25)
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Drop support for Rails 6.1 [#8449](https://github.com/activeadmin/activeadmin/pull/8449)
|
||||
- Drop Ruby 3.0 compatibility [#8489](https://github.com/activeadmin/activeadmin/pull/8489)
|
||||
|
||||
### Enhancements ✨
|
||||
- Add polish translations for search status [#8487](https://github.com/activeadmin/activeadmin/pull/8487)
|
||||
- Update zh-TW & ja locale with new v4 keys [#8521](https://github.com/activeadmin/activeadmin/pull/8521)
|
||||
- Update zh-CN locale with new v4 keys [#8546](https://github.com/activeadmin/activeadmin/pull/8546)
|
||||
- Improve v3 docs regarding compatibility with vite_rails [#8549](https://github.com/activeadmin/activeadmin/pull/8549)
|
||||
- Remove redundant safe navigation operator [#8528](https://github.com/activeadmin/activeadmin/pull/8528)
|
||||
- Use safe navigation for `pundit_default_policy` [#8530](https://github.com/activeadmin/activeadmin/pull/8530)
|
||||
- Use safe navigation operator in layout helper [#8533](https://github.com/activeadmin/activeadmin/pull/8533)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- run update_resource inside a transaction to avoid autosaving relationships through assign_attributes when the record is invalid [#7437](https://github.com/activeadmin/activeadmin/pull/7437)
|
||||
- Fix attributes passed to form has_many not being set on new record form items [#8550](https://github.com/activeadmin/activeadmin/pull/8550)
|
||||
- Convert plugin.js to ESM because package.json has type set to module [#8536](https://github.com/activeadmin/activeadmin/pull/8536)
|
||||
|
||||
### Other Changes 🛠
|
||||
- Migrate docs to VitePress [#8194](https://github.com/activeadmin/activeadmin/pull/8194)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta13...v4.0.0.beta14](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta13...v4.0.0.beta14)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta13 (2024-09-24)
|
||||
|
||||
### Enhancements ✨
|
||||
- Add polish translations for v4 keys [#8481](https://github.com/activeadmin/activeadmin/pull/8481)
|
||||
- Add spanish translations for v4 keys [#8483](https://github.com/activeadmin/activeadmin/pull/8483)
|
||||
- Prefer `require_relative` for internal requires [#8482](https://github.com/activeadmin/activeadmin/pull/8482)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta12...v4.0.0.beta13](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta12...v4.0.0.beta13)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta12 (2024-09-15)
|
||||
|
||||
### Enhancements ✨
|
||||
- Bump `inherited_resources` requirement to `~> 2.0` [#8477](https://github.com/activeadmin/activeadmin/pull/8477)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta11...v4.0.0.beta12](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta11...v4.0.0.beta12)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta11 (2024-08-31)
|
||||
|
||||
### Enhancements ✨
|
||||
- Unify specifying label in attributes_table component [#8458](https://github.com/activeadmin/activeadmin/pull/8458)
|
||||
- Optimize count query for `pagination_total: false` option [#6911](https://github.com/activeadmin/activeadmin/pull/6911)
|
||||
- Use attribute_types instead of columns_hash to determine type [#8457](https://github.com/activeadmin/activeadmin/pull/8457)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Use consistent text-like inputs selector list for CSS styles [#8456](https://github.com/activeadmin/activeadmin/pull/8456)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta10...v4.0.0.beta11](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta10...v4.0.0.beta11)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta10 (2024-08-24)
|
||||
|
||||
### Template Updates 📝
|
||||
- Add host app's layouts/active_admin to tailwind config [#8430](https://github.com/activeadmin/activeadmin/pull/8430)
|
||||
|
||||
### Enhancements ✨
|
||||
- Support async_count for scopes [#8394](https://github.com/activeadmin/activeadmin/pull/8394)
|
||||
- Add tbody_html and row_html options to TableFor and IndexAsTable [#8423](https://github.com/activeadmin/activeadmin/pull/8423)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Improve form f.inputs attributes rendering [#8439](https://github.com/activeadmin/activeadmin/pull/8439), [#8448](https://github.com/activeadmin/activeadmin/pull/8448)
|
||||
- Fix batch action with partial and no confirm doesn't submit the form on click [#8442](https://github.com/activeadmin/activeadmin/pull/8442)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta9...v4.0.0.beta10](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta9...v4.0.0.beta10)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta9 (2024-08-04)
|
||||
|
||||
### Template Updates 📝
|
||||
- Add accessible names for navigation buttons [#8338](https://github.com/activeadmin/activeadmin/pull/8338)
|
||||
|
||||
### Enhancements ✨
|
||||
- Better implementation of counter-cache-column check [#8411](https://github.com/activeadmin/activeadmin/pull/8411)
|
||||
|
||||
### Other Changes 🛠
|
||||
- Fix url redirects in documentation [#8407](https://github.com/activeadmin/activeadmin/pull/8407)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta8...v4.0.0.beta9](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta8...v4.0.0.beta9)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta8 (2024-07-27)
|
||||
|
||||
### Enhancements ✨
|
||||
- Update Catalan translation [#8356](https://github.com/activeadmin/activeadmin/pull/8356)
|
||||
- Allow batch actions without a confirmation dialog [#8389](https://github.com/activeadmin/activeadmin/pull/8389)
|
||||
- Fix styles for select[multiple] form controls [#8406](https://github.com/activeadmin/activeadmin/pull/8406)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta7...v4.0.0.beta8](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta7...v4.0.0.beta8)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta7 (2024-05-31)
|
||||
|
||||
### Enhancements ✨
|
||||
- Update de locale with new v4 keys [#8325](https://github.com/activeadmin/activeadmin/pull/8325)
|
||||
- Remove de-CH locale [#8326](https://github.com/activeadmin/activeadmin/pull/8326)
|
||||
|
||||
### Security Fixes 🔒
|
||||
- Fix potential XSS issue when rendering form legends [#8348](https://github.com/activeadmin/activeadmin/pull/8348)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta6...v4.0.0.beta7](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta6...v4.0.0.beta7)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta6 (2024-05-03)
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Drop Ruby 2.7 support [#8259](https://github.com/activeadmin/activeadmin/pull/8259)
|
||||
|
||||
### Template Updates 📝
|
||||
- Use Tailwind's darkMode selector strategy [#8264](https://github.com/activeadmin/activeadmin/pull/8264)
|
||||
|
||||
### Enhancements ✨
|
||||
- Update zh-CN locale with new v4 keys [#8284](https://github.com/activeadmin/activeadmin/pull/8284)
|
||||
- Update zh-TW locale with new v4 keys [#8297](https://github.com/activeadmin/activeadmin/pull/8297)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Add csv dependency to fix Ruby 3.3 warning [#8303](https://github.com/activeadmin/activeadmin/pull/8303)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta5...v4.0.0.beta6](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta5...v4.0.0.beta6)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta5 (2024-02-14)
|
||||
|
||||
### Enhancements ✨
|
||||
- Improve Italian translation [#8235](https://github.com/activeadmin/activeadmin/pull/8235)
|
||||
- Bump flowbite to v2.3.0 [#8257](https://github.com/activeadmin/activeadmin/pull/8257)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Use an anchor for tabs component toggle [#8242](https://github.com/activeadmin/activeadmin/pull/8242)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta4...v4.0.0.beta5](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta4...v4.0.0.beta5)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta4 (2024-01-17)
|
||||
|
||||
### Template Updates 📝
|
||||
- Move pagination translations to active_admin scope [#8218](https://github.com/activeadmin/activeadmin/pull/8218)
|
||||
|
||||
### Enhancements ✨
|
||||
- Vendor rails-ujs ESM JS [#8217](https://github.com/activeadmin/activeadmin/pull/8217)
|
||||
- Include vendor JS files in NPM package [#8221](https://github.com/activeadmin/activeadmin/pull/8221)
|
||||
- Remove i18n-tasks.yml from gem release [#8227](https://github.com/activeadmin/activeadmin/pull/8227)
|
||||
- Make importmap-rails optional [#8228](https://github.com/activeadmin/activeadmin/pull/8228)
|
||||
- Exclude primary key from generated resource [#8232](https://github.com/activeadmin/activeadmin/pull/8232)
|
||||
- Make NPM package compatible with tools like vite_rails [#8234](https://github.com/activeadmin/activeadmin/pull/8234)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
- Ensure form presenter is applied on create/update actions [#8238](https://github.com/activeadmin/activeadmin/pull/8238)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta3...v4.0.0.beta4](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta3...v4.0.0.beta4)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta3 (2024-01-06)
|
||||
|
||||
- Include additional files in gem release [#8212](https://github.com/activeadmin/activeadmin/pull/8212)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta2...v4.0.0.beta3](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta2...v4.0.0.beta3)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta2 (2024-01-06)
|
||||
|
||||
- Include vendor directory in gem release [#8211](https://github.com/activeadmin/activeadmin/pull/8211)
|
||||
|
||||
**Full Changelog**: [v4.0.0.beta1...v4.0.0.beta2](https://github.com/activeadmin/activeadmin/compare/v4.0.0.beta1...v4.0.0.beta2)
|
||||
|
||||
---
|
||||
|
||||
## v4.0.0.beta1 (2024-01-06)
|
||||
|
||||
**First beta release of ActiveAdmin v4!**
|
||||
|
||||
This release represents a complete redesign using TailwindCSS with **mobile web, dark mode and RTL support** and a default, **customizable theme through partials**.
|
||||
|
||||
### Key Features
|
||||
- **TailwindCSS**: Complete migration to TailwindCSS
|
||||
- **Mobile Support**: Fully responsive design
|
||||
- **Dark Mode**: Built-in dark mode support
|
||||
- **RTL Support**: Right-to-left language support
|
||||
- **ESM JavaScript**: Modern ES modules
|
||||
- **importmap-rails**: Uses importmap-rails for JS assets
|
||||
- **Customizable Theme**: Theme customization through partials
|
||||
|
||||
### Breaking Changes 🚨
|
||||
- Requires `cssbundling-rails` and `importmap-rails`
|
||||
- Removed all SCSS files (sassc-rails dependency removed)
|
||||
- Complete UI/UX redesign
|
||||
|
||||
### Major Changes
|
||||
- Prep Tailwind CSS migration (multiple PRs: #8035, #8036, #8037, #8116, #8122, #8139, #8155, #8170)
|
||||
- Remove old, since replaced JS [#8040](https://github.com/activeadmin/activeadmin/pull/8040)
|
||||
- Replace html/assets related configs with simple partials [#8156](https://github.com/activeadmin/activeadmin/pull/8156)
|
||||
- Remove sassc-rails and all SCSS files [#8157](https://github.com/activeadmin/activeadmin/pull/8157)
|
||||
- Extract headers, menu and sidebar components to partials [#8162](https://github.com/activeadmin/activeadmin/pull/8162)
|
||||
- Use ES format for JS [#8171](https://github.com/activeadmin/activeadmin/pull/8171)
|
||||
- Extract layout and pages to partials [#8172](https://github.com/activeadmin/activeadmin/pull/8172)
|
||||
- Use importmap-rails for JS assets [#8186](https://github.com/activeadmin/activeadmin/pull/8186)
|
||||
- Move kaminari templates to active_admin folder [#8190](https://github.com/activeadmin/activeadmin/pull/8190)
|
||||
- Improve RTL support all around [#8196](https://github.com/activeadmin/activeadmin/pull/8196)
|
||||
- Migrate view helpers to app/helpers path [#8202](https://github.com/activeadmin/activeadmin/pull/8202)
|
||||
|
||||
### Enhancements ✨
|
||||
- Provide detail in DB statement timeout error for filters [#8117](https://github.com/activeadmin/activeadmin/pull/8117)
|
||||
- Use active_admin_authorization as Ransack auth_object option [#8143](https://github.com/activeadmin/activeadmin/pull/8143)
|
||||
- Update FR locales [#8193](https://github.com/activeadmin/activeadmin/pull/8193)
|
||||
- Update nl.yml for v4 [#8195](https://github.com/activeadmin/activeadmin/pull/8195)
|
||||
|
||||
### Other Changes 🛠
|
||||
- Introduce RuboCop Performance [#8024](https://github.com/activeadmin/activeadmin/pull/8024)
|
||||
- Remove multiline ternary operator [#8034](https://github.com/activeadmin/activeadmin/pull/8034)
|
||||
- Prefer `match?` over `=~` to avoid MatchData [#8138](https://github.com/activeadmin/activeadmin/pull/8138)
|
||||
|
||||
**Full Changelog**: [v3.2.0...v4.0.0.beta1](https://github.com/activeadmin/activeadmin/compare/v3.2.0...v4.0.0.beta1)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Minimum Requirements (as of v4.0.0.beta20):**
|
||||
- Rails 7.2+
|
||||
- Ruby 3.2+
|
||||
- cssbundling-rails
|
||||
- importmap-rails (optional as of beta4)
|
||||
|
||||
**Key Migration Path:**
|
||||
The v4.0.0 beta series represents a complete redesign of ActiveAdmin using TailwindCSS. The migration requires:
|
||||
1. Installing `cssbundling-rails` and configuring TailwindCSS
|
||||
2. Installing `importmap-rails` (optional since beta4)
|
||||
3. Running the assets generator
|
||||
4. Updating custom CSS/JS from the old SCSS/jQuery patterns
|
||||
5. Reviewing the [Upgrading guide](https://github.com/activeadmin/activeadmin/blob/master/UPGRADING.md)
|
||||
|
||||
See [demo app](https://github.com/activeadmin/demo.activeadmin.info) for a working example.
|
||||
136
docs/activeadmin-tailwind-4.md
Обычный файл
136
docs/activeadmin-tailwind-4.md
Обычный файл
@@ -0,0 +1,136 @@
|
||||
# ActiveAdmin + Tailwind CSS v4 Guide
|
||||
|
||||
This guide consolidates the ActiveAdmin 4.0.0.beta20 upgrade notes with a working Tailwind v4 build setup. Use it as a template when updating other ActiveAdmin extension gems.
|
||||
|
||||
## What Changed in ActiveAdmin 4.0.0.beta20 (from UPGRADING.md)
|
||||
|
||||
### Version Requirements
|
||||
- Rails 7.2+ (Rails 7.0 and 7.1 are no longer supported)
|
||||
- Ruby 3.2+ (Ruby 3.0 and 3.1 are no longer supported)
|
||||
|
||||
### Template Updates
|
||||
- `_site_header.html.erb` container class changed from `sticky` to `fixed`.
|
||||
- `active_admin.html.erb` now includes the `pt-16` utility class.
|
||||
|
||||
### Tailwind v4 Updates
|
||||
Replace the old Tailwind directives with the new v4 import and config:
|
||||
|
||||
```diff
|
||||
-@tailwind base;
|
||||
-@tailwind components;
|
||||
-@tailwind utilities;
|
||||
+@import "tailwindcss";
|
||||
+
|
||||
+@config "../../../tailwind-active_admin.config.mjs";
|
||||
```
|
||||
|
||||
Update your CSS build command:
|
||||
|
||||
```diff
|
||||
-"build:css": "tailwindcss -i ./app/assets/stylesheets/active_admin.css -o ./app/assets/builds/active_admin.css --minify -c tailwind-active_admin.config.js"
|
||||
+"build:css": "npx @tailwindcss/cli -i ./app/assets/stylesheets/active_admin.css -o ./app/assets/builds/active_admin.css --minify"
|
||||
```
|
||||
|
||||
If you use `tailwindcss-rails`, prefer the bundled `bin/tailwindcss` and the `@config` directive instead of passing `-c`.
|
||||
|
||||
### ESM Config Warning
|
||||
Tailwind config now uses ESM. To avoid the warning:
|
||||
- Rename `tailwind-active_admin.config.js` to `tailwind-active_admin.config.mjs`, or
|
||||
- Add `"type": "module"` to `package.json`.
|
||||
|
||||
### Breaking Changes Summary
|
||||
- jQuery and jQuery UI removed.
|
||||
- `columns` component removed (use Tailwind grid).
|
||||
- `tabs` component removed.
|
||||
- Replace `default_main_content` with `render "show_default"`.
|
||||
- Replace `as: :datepicker` with `as: :date_picker`.
|
||||
- Replace `active_admin_comments` with `active_admin_comments_for(resource)`.
|
||||
- Replace `attributes_table` with `attributes_table_for(resource)` in sidebars.
|
||||
- Removed `IndexAsBlog`, `IndexAsBlock`, `IndexAsGrid`.
|
||||
- Batch action form DSL replaced with partial support.
|
||||
- Deeply nested submenus removed (only one level supported).
|
||||
- Removed `Panel#header_action` and `index_column`.
|
||||
|
||||
### New in beta20
|
||||
- Parent menu item linking support
|
||||
- Improved comments pagination styling
|
||||
- Better vertical spacing for has-many forms
|
||||
- Pointer cursor restored on eligible buttons
|
||||
- Various accessibility improvements
|
||||
|
||||
## Recommended Build Setup (tailwindcss-rails)
|
||||
|
||||
### 1. Gemfile
|
||||
|
||||
```ruby
|
||||
gem "activeadmin", "4.0.0.beta20"
|
||||
gem "tailwindcss-rails", "~> 4.4.0"
|
||||
```
|
||||
|
||||
### 2. Tailwind Input
|
||||
|
||||
```css
|
||||
/* app/assets/stylesheets/active_admin.tailwind.css */
|
||||
@import "tailwindcss";
|
||||
@config "../../../tailwind-active_admin.config.mjs";
|
||||
```
|
||||
|
||||
### 3. Tailwind Config
|
||||
|
||||
```javascript
|
||||
// tailwind-active_admin.config.mjs
|
||||
import { execSync } from "child_process";
|
||||
import activeAdminPlugin from "@activeadmin/activeadmin/plugin";
|
||||
|
||||
const activeAdminPath = execSync("bundle show activeadmin", {
|
||||
encoding: "utf-8"
|
||||
}).trim();
|
||||
|
||||
export default {
|
||||
content: [
|
||||
`${activeAdminPath}/vendor/javascript/flowbite.js`,
|
||||
`${activeAdminPath}/plugin.js`,
|
||||
`${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`,
|
||||
"./app/admin/**/*.{arb,erb,html,rb}",
|
||||
"./app/views/active_admin/**/*.{arb,erb,html,rb}`,
|
||||
"./app/views/admin/**/*.{arb,erb,html,rb}`,
|
||||
"./app/views/layouts/active_admin*.{erb,html}`,
|
||||
"./app/javascript/**/*.js"
|
||||
],
|
||||
darkMode: "selector",
|
||||
plugins: [activeAdminPlugin]
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Build Tasks
|
||||
|
||||
```ruby
|
||||
# lib/tasks/active_admin.rake
|
||||
namespace :active_admin do
|
||||
desc "Build Active Admin Tailwind stylesheets"
|
||||
task build: :environment do
|
||||
command = [
|
||||
Rails.root.join("bin/tailwindcss").to_s,
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 5. Tailwind CLI Binstub
|
||||
|
||||
```bash
|
||||
bundle binstubs tailwindcss-ruby --force
|
||||
```
|
||||
|
||||
## Notes for Extension Gems
|
||||
|
||||
When updating other ActiveAdmin extension gems:
|
||||
- Keep all JS/CSS inside the gem or npm package; do not copy large files into host apps.
|
||||
- Add only a thin import to the host app (`import "your_gem"`).
|
||||
- Use Tailwind v4 `@import` + `@config` and ESM config files.
|
||||
- Avoid jQuery-based plugins or require them explicitly.
|
||||
141
docs/setup-activeadmin-app.md
Обычный файл
141
docs/setup-activeadmin-app.md
Обычный файл
@@ -0,0 +1,141 @@
|
||||
# Setting Up a Standalone ActiveAdmin 4 App (Tailwind v4)
|
||||
|
||||
This guide creates a new Rails app with ActiveAdmin 4.0.0.beta20, Tailwind CSS v4, and optional AA extension gems.
|
||||
|
||||
## 1. Create Rails App
|
||||
|
||||
```bash
|
||||
rails new my_admin_app --css=tailwind --javascript=esbuild
|
||||
cd my_admin_app
|
||||
```
|
||||
|
||||
## 2. Gemfile
|
||||
|
||||
```ruby
|
||||
# ActiveAdmin 4 beta
|
||||
gem "activeadmin", "4.0.0.beta20"
|
||||
|
||||
# Required by ActiveAdmin 4
|
||||
gem "importmap-rails", "~> 2.0"
|
||||
gem "propshaft"
|
||||
|
||||
# Tailwind CLI (v4)
|
||||
gem "tailwindcss-rails", "~> 4.4.0"
|
||||
```
|
||||
|
||||
```bash
|
||||
bundle install
|
||||
```
|
||||
|
||||
## 3. Install ActiveAdmin
|
||||
|
||||
```bash
|
||||
rails generate active_admin:install
|
||||
rails db:create db:migrate db:seed
|
||||
```
|
||||
|
||||
## 4. Install ActiveAdmin Assets
|
||||
|
||||
```bash
|
||||
rails generate active_admin:assets
|
||||
```
|
||||
|
||||
This creates:
|
||||
- `app/assets/stylesheets/active_admin.css`
|
||||
- `tailwind-active_admin.config.js` (ESM)
|
||||
|
||||
Rename to `.mjs` for Tailwind v4:
|
||||
|
||||
```bash
|
||||
mv tailwind-active_admin.config.js tailwind-active_admin.config.mjs
|
||||
```
|
||||
|
||||
## 5. Tailwind Input (ActiveAdmin)
|
||||
|
||||
Create or update the Tailwind input file:
|
||||
|
||||
```css
|
||||
/* app/assets/stylesheets/active_admin.tailwind.css */
|
||||
@import "tailwindcss";
|
||||
@config "../../../tailwind-active_admin.config.mjs";
|
||||
```
|
||||
|
||||
## 6. Build Tasks
|
||||
|
||||
```ruby
|
||||
# lib/tasks/active_admin.rake
|
||||
namespace :active_admin do
|
||||
desc "Build Active Admin Tailwind stylesheets"
|
||||
task build: :environment do
|
||||
command = [
|
||||
Rails.root.join("bin/tailwindcss").to_s,
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
|
||||
desc "Watch Active Admin Tailwind stylesheets"
|
||||
task watch: :environment do
|
||||
command = [
|
||||
Rails.root.join("bin/tailwindcss").to_s,
|
||||
"--watch",
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
end
|
||||
|
||||
Rake::Task["assets:precompile"].enhance(["active_admin:build"])
|
||||
```
|
||||
|
||||
## 7. JavaScript Entry
|
||||
|
||||
```javascript
|
||||
// app/javascript/active_admin.js
|
||||
import "@activeadmin/activeadmin";
|
||||
```
|
||||
|
||||
## 8. Build Assets
|
||||
|
||||
```bash
|
||||
bundle binstubs tailwindcss-ruby --force
|
||||
npm install @activeadmin/activeadmin@^4.0.0-beta20
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 9. Start Server
|
||||
|
||||
```bash
|
||||
rails server
|
||||
```
|
||||
|
||||
Visit http://localhost:3000/admin
|
||||
|
||||
## ActiveAdmin 4.0.0.beta20 Notes (from upgrade guide)
|
||||
|
||||
**Version Requirements:**
|
||||
- Rails 7.2+ (Rails 7.0 and 7.1 are no longer supported)
|
||||
- Ruby 3.2+ (Ruby 3.0 and 3.1 are no longer supported)
|
||||
|
||||
**Breaking Changes from earlier v4 betas:**
|
||||
- `_site_header.html.erb` container class changed from `sticky` to `fixed`.
|
||||
- `active_admin.html.erb` adds the `pt-16` utility class.
|
||||
- Tailwind v4 requires `@import "tailwindcss"` + `@config`.
|
||||
- jQuery and jQuery UI removed; `columns` and `tabs` components removed.
|
||||
- Replace `default_main_content` with `render "show_default"`.
|
||||
- Replace `as: :datepicker` with `as: :date_picker`.
|
||||
- Replace `active_admin_comments` with `active_admin_comments_for(resource)`.
|
||||
- Replace `attributes_table` with `attributes_table_for(resource)` in sidebars.
|
||||
|
||||
**New in beta20:**
|
||||
- Parent menu item linking support
|
||||
- Improved comments pagination styling
|
||||
- Better vertical spacing for has-many forms
|
||||
- Pointer cursor restored on eligible buttons
|
||||
- Various accessibility improvements
|
||||
187
docs/setup-activeadmin-gem.md
Обычный файл
187
docs/setup-activeadmin-gem.md
Обычный файл
@@ -0,0 +1,187 @@
|
||||
# Setting Up an ActiveAdmin 4 Extension Gem (Combustion)
|
||||
|
||||
This guide mirrors the working setup used in this repo: ActiveAdmin 4.0.0.beta20 + Tailwind CSS v4 + test app.
|
||||
|
||||
## 1. Gem Dependencies
|
||||
|
||||
```ruby
|
||||
# Gemfile
|
||||
source "https://rubygems.org"
|
||||
gemspec
|
||||
|
||||
gem "rails", "~> 7.2"
|
||||
gem "activeadmin", "4.0.0.beta20"
|
||||
gem "sqlite3"
|
||||
gem "puma"
|
||||
|
||||
gem "importmap-rails", "~> 2.0"
|
||||
gem "propshaft"
|
||||
|
||||
gem "tailwindcss-rails", "~> 4.4.0"
|
||||
|
||||
group :development, :test do
|
||||
gem "combustion", "~> 1.3"
|
||||
gem "rspec-rails"
|
||||
gem "capybara"
|
||||
end
|
||||
```
|
||||
|
||||
## 2. Combustion Test App
|
||||
|
||||
```bash
|
||||
bundle exec combust
|
||||
```
|
||||
|
||||
## 3. config.ru Loading Order
|
||||
|
||||
```ruby
|
||||
# config.ru
|
||||
require "rubygems"
|
||||
require "bundler"
|
||||
|
||||
Bundler.setup(:default, :development)
|
||||
|
||||
require "combustion"
|
||||
Combustion.initialize! :active_record, :action_controller, :action_view do
|
||||
config.load_defaults Rails::VERSION::STRING.to_f if Rails::VERSION::MAJOR >= 7
|
||||
end
|
||||
|
||||
require "importmap-rails"
|
||||
require "active_admin"
|
||||
require "activeadmin_quill_editor"
|
||||
|
||||
run Combustion::Application
|
||||
```
|
||||
|
||||
## 4. Internal App Structure
|
||||
|
||||
```bash
|
||||
cd spec/internal
|
||||
mkdir -p app/admin app/assets/{builds,config,stylesheets} app/javascript
|
||||
```
|
||||
|
||||
## 5. ActiveAdmin Setup
|
||||
|
||||
```ruby
|
||||
# spec/internal/config/initializers/active_admin.rb
|
||||
ActiveAdmin.setup do |config|
|
||||
config.site_title = "Test App"
|
||||
config.authentication_method = false
|
||||
config.current_user_method = false
|
||||
end
|
||||
```
|
||||
|
||||
## 6. Tailwind Input + Config
|
||||
|
||||
```css
|
||||
/* spec/internal/app/assets/stylesheets/active_admin.tailwind.css */
|
||||
@import "tailwindcss";
|
||||
@config "../../../tailwind-active_admin.config.mjs";
|
||||
|
||||
/* Import Quill Editor CSS */
|
||||
@import "activeadmin/quill_editor/quill.snow";
|
||||
@import "activeadmin/quill_editor_input";
|
||||
```
|
||||
|
||||
```javascript
|
||||
// spec/internal/tailwind-active_admin.config.mjs
|
||||
import { execSync } from "child_process";
|
||||
import activeAdminPlugin from "@activeadmin/activeadmin/plugin";
|
||||
|
||||
const activeAdminPath = execSync("bundle show activeadmin", {
|
||||
encoding: "utf-8"
|
||||
}).trim();
|
||||
|
||||
export default {
|
||||
content: [
|
||||
`${activeAdminPath}/vendor/javascript/flowbite.js`,
|
||||
`${activeAdminPath}/plugin.js`,
|
||||
`${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`,
|
||||
"./app/admin/**/*.{arb,erb,html,rb}`,
|
||||
"./app/views/**/*.{arb,erb,html,rb}`,
|
||||
"./app/javascript/**/*.js",
|
||||
"../../app/assets/**/*.{js,css}"
|
||||
],
|
||||
darkMode: "selector",
|
||||
plugins: [activeAdminPlugin]
|
||||
};
|
||||
```
|
||||
|
||||
## 7. Tailwind Build Tasks
|
||||
|
||||
```ruby
|
||||
# spec/internal/lib/tasks/active_admin.rake
|
||||
namespace :active_admin do
|
||||
desc "Build Active Admin Tailwind stylesheets"
|
||||
task build: :environment do
|
||||
command = [
|
||||
Rails.root.join("bin/tailwindcss").to_s,
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## 8. Tailwind Binstub
|
||||
|
||||
```bash
|
||||
bundle binstubs tailwindcss-ruby --force
|
||||
```
|
||||
|
||||
## 9. JS Entry
|
||||
|
||||
```javascript
|
||||
// spec/internal/app/javascript/active_admin.js
|
||||
import "@activeadmin/activeadmin";
|
||||
|
||||
import Quill from "quill";
|
||||
window.Quill = Quill;
|
||||
|
||||
import "activeadmin_quill_editor";
|
||||
```
|
||||
|
||||
## 10. package.json (internal)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "internal",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:js": "node esbuild.config.js",
|
||||
"build:css": "bundle exec rake active_admin:build",
|
||||
"build": "npm run build:js && npm run build:css"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@activeadmin/activeadmin": "^4.0.0-beta20",
|
||||
"quill": "^2.0.3",
|
||||
"esbuild": "^0.24.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 11. ActiveAdmin 4.0.0.beta20 Notes (from upgrade guide)
|
||||
|
||||
**Version Requirements:**
|
||||
- Rails 7.2+ (Rails 7.0 and 7.1 are no longer supported)
|
||||
- Ruby 3.2+ (Ruby 3.0 and 3.1 are no longer supported)
|
||||
|
||||
**Breaking Changes from earlier v4 betas:**
|
||||
- `_site_header.html.erb` container class changed from `sticky` to `fixed`.
|
||||
- `active_admin.html.erb` adds the `pt-16` utility class.
|
||||
- Tailwind v4 requires `@import "tailwindcss"` + `@config`.
|
||||
- jQuery and jQuery UI removed; `columns` and `tabs` components removed.
|
||||
- Replace `default_main_content` with `render "show_default"`.
|
||||
- Replace `as: :datepicker` with `as: :date_picker`.
|
||||
- Replace `active_admin_comments` with `active_admin_comments_for(resource)`.
|
||||
- Replace `attributes_table` with `attributes_table_for(resource)` in sidebars.
|
||||
|
||||
**New in beta20:**
|
||||
- Parent menu item linking support
|
||||
- Improved comments pagination styling
|
||||
- Better vertical spacing for has-many forms
|
||||
- Pointer cursor restored on eligible buttons
|
||||
- Various accessibility improvements
|
||||
525
docs/update-quill.md
Обычный файл
525
docs/update-quill.md
Обычный файл
@@ -0,0 +1,525 @@
|
||||
# Quill Editor Migration Guide for ActiveAdmin 4
|
||||
|
||||
## Overview
|
||||
This guide helps you migrate the activeadmin-quill_editor gem to support ActiveAdmin 4 with modern Rails 8+ and Propshaft asset pipeline.
|
||||
|
||||
## Key Changes Made
|
||||
|
||||
### 1. Removed jQuery Dependency
|
||||
**Before**: JavaScript relied on jQuery for initialization and DOM manipulation
|
||||
**After**: Pure vanilla JavaScript with modern event listeners
|
||||
|
||||
### 2. Asset Pipeline Updates
|
||||
**Before**: Sprockets-based asset compilation with CDN dependencies
|
||||
**After**: Propshaft-compatible with NPM-based Quill installation
|
||||
|
||||
### 3. Ruby and Rails Version Requirements
|
||||
- **Minimum Ruby**: 3.2+ (previously 3.0+)
|
||||
- **Rails Support**: 7.0+ with ActiveAdmin 2.9-4.x
|
||||
- **ActiveAdmin 4**: Full support with Propshaft
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
### Step 1: Update Your Gemfile
|
||||
```ruby
|
||||
gem 'activeadmin_quill_editor', '~> 2.0'
|
||||
```
|
||||
|
||||
### Step 2: Install Quill via NPM
|
||||
```bash
|
||||
# In your Rails app root
|
||||
npm install quill@^2.0.2
|
||||
```
|
||||
|
||||
Or add to `package.json`:
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"quill": "^2.0.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Configure JavaScript (Rails 8 with esbuild)
|
||||
|
||||
**IMPORTANT**: Do NOT copy the gem's JavaScript file into your application. The gem provides `activeadmin_quill_editor.js` via its vendor/assets directory. You should import it, not duplicate it.
|
||||
|
||||
Create or update `app/javascript/active_admin.js`:
|
||||
```javascript
|
||||
// Import ActiveAdmin - this already includes all features and Rails UJS
|
||||
// DO NOT import Rails separately as it's already included and started in ActiveAdmin
|
||||
import '@activeadmin/activeadmin';
|
||||
|
||||
// Import Quill from NPM and make it globally available
|
||||
import Quill from 'quill';
|
||||
window.Quill = Quill;
|
||||
|
||||
// Import the image uploader plugin if available (optional)
|
||||
try {
|
||||
const ImageUploader = require('quill-image-uploader');
|
||||
window.ImageUploader = ImageUploader.default || ImageUploader;
|
||||
} catch(e) {
|
||||
// Image uploader is optional (silent failure)
|
||||
}
|
||||
|
||||
// Import the Quill Editor initialization module from the gem
|
||||
// This imports from vendor/assets/javascripts/activeadmin/quill_editor.js
|
||||
import QuillEditorModule from 'activeadmin/quill_editor';
|
||||
|
||||
// Now that Quill is available, initialize the editors
|
||||
// This ensures proper initialization order without setTimeout hacks
|
||||
if (window.QuillEditor && window.QuillEditor.init) {
|
||||
window.QuillEditor.init();
|
||||
} else if (QuillEditorModule && QuillEditorModule.init) {
|
||||
QuillEditorModule.init();
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Configure CSS
|
||||
|
||||
#### With Tailwind CSS (ActiveAdmin 4)
|
||||
In `app/assets/stylesheets/active_admin.tailwind.css`:
|
||||
```css
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
/* Quill styles */
|
||||
@import "quill/dist/quill.snow.css";
|
||||
@import "quill/dist/quill.bubble.css";
|
||||
```
|
||||
|
||||
#### With Sass/SCSS (Legacy)
|
||||
In `app/assets/stylesheets/active_admin.scss`:
|
||||
```scss
|
||||
@import "active_admin/base";
|
||||
@import "quill/dist/quill.snow";
|
||||
// Optional bubble theme
|
||||
@import "quill/dist/quill.bubble";
|
||||
```
|
||||
|
||||
### Step 5: Build Configuration
|
||||
|
||||
#### esbuild.config.js
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
const esbuild = require('esbuild');
|
||||
const path = require('path');
|
||||
|
||||
// IMPORTANT: Set up the alias to resolve the gem's JavaScript file
|
||||
// For production apps, you can get the gem path dynamically:
|
||||
const { execSync } = require('child_process');
|
||||
const gemPath = execSync('bundle show activeadmin_quill_editor', { encoding: 'utf-8' }).trim();
|
||||
// Or for development/testing with a local gem:
|
||||
// const gemPath = path.resolve(__dirname, '../..'); // Adjust based on your setup
|
||||
|
||||
// Configuration for esbuild with proper module resolution
|
||||
const config = {
|
||||
entryPoints: ['app/javascript/active_admin.js'],
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
outdir: 'app/assets/builds',
|
||||
publicPath: '/assets',
|
||||
loader: {
|
||||
'.js': 'js',
|
||||
},
|
||||
// Define global Quill for the initialization script
|
||||
define: {
|
||||
'global': 'window'
|
||||
},
|
||||
// CRITICAL: Use alias to import the gem's JavaScript from vendor/assets
|
||||
alias: {
|
||||
'activeadmin/quill_editor': path.join(gemPath, 'vendor/assets/javascripts/activeadmin/quill_editor.js')
|
||||
}
|
||||
};
|
||||
|
||||
// Check if we're in watch mode
|
||||
const watchMode = process.argv.includes('--watch');
|
||||
|
||||
if (watchMode) {
|
||||
// Start the build with watch mode
|
||||
esbuild.context(config).then(ctx => {
|
||||
ctx.watch();
|
||||
console.log('Watching for changes...');
|
||||
}).catch(error => {
|
||||
console.error('Build failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
// Single build
|
||||
esbuild.build(config).then(() => {
|
||||
console.log('Build completed');
|
||||
}).catch(error => {
|
||||
console.error('Build failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### package.json scripts
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build:js": "node esbuild.config.js",
|
||||
"build:css": "tailwindcss -i app/assets/stylesheets/active_admin.tailwind.css -o app/assets/builds/active_admin.css -m",
|
||||
"build": "npm run build:js && npm run build:css"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example Files
|
||||
|
||||
These are complete working examples from our test suite that you can use as reference:
|
||||
|
||||
### tailwind.config.js
|
||||
```javascript
|
||||
const execSync = require('child_process').execSync;
|
||||
const activeAdminPath = execSync('bundle show activeadmin', { encoding: 'utf-8' }).trim();
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
`${activeAdminPath}/vendor/javascript/flowbite.js`,
|
||||
`${activeAdminPath}/plugin.js`,
|
||||
`${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`,
|
||||
'./app/admin/**/*.{arb,erb,html,rb}',
|
||||
'./app/views/active_admin/**/*.{arb,erb,html,rb}',
|
||||
'./app/views/admin/**/*.{arb,erb,html,rb}',
|
||||
'./app/javascript/**/*.js',
|
||||
// Quill editor gem files
|
||||
'../../lib/**/*.rb',
|
||||
'../../app/**/*.rb'
|
||||
],
|
||||
darkMode: "class",
|
||||
plugins: [
|
||||
require('@activeadmin/activeadmin/plugin')
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
// Add any custom theme extensions here
|
||||
}
|
||||
},
|
||||
// CRITICAL: Safelist for ActiveAdmin 4 dynamic classes
|
||||
safelist: [
|
||||
// Grid and layout
|
||||
'grid', 'gap-4', 'gap-6', 'lg:grid-cols-3', 'md:grid-cols-2',
|
||||
'col-span-2', 'col-span-3', 'lg:col-span-2', 'lg:col-span-1',
|
||||
// Flexbox
|
||||
'flex', 'inline-flex', 'flex-col', 'flex-row', 'flex-wrap', 'flex-nowrap',
|
||||
'justify-start', 'justify-end', 'justify-center', 'justify-between', 'justify-around',
|
||||
'items-start', 'items-end', 'items-center', 'items-baseline', 'items-stretch',
|
||||
// Spacing
|
||||
'space-x-4', 'space-y-4', 'space-x-2', 'space-y-2',
|
||||
'p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-5', 'p-6', 'p-8',
|
||||
'px-0', 'px-1', 'px-2', 'px-3', 'px-4', 'px-5', 'px-6', 'px-8',
|
||||
'py-0', 'py-1', 'py-2', 'py-3', 'py-4', 'py-5', 'py-6', 'py-8',
|
||||
'm-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-5', 'm-6', 'm-8',
|
||||
'mx-0', 'mx-1', 'mx-2', 'mx-3', 'mx-4', 'mx-5', 'mx-6', 'mx-8', 'mx-auto',
|
||||
'my-0', 'my-1', 'my-2', 'my-3', 'my-4', 'my-5', 'my-6', 'my-8', 'my-auto',
|
||||
'mt-0', 'mt-1', 'mt-2', 'mt-3', 'mt-4', 'mt-5', 'mt-6', 'mt-8',
|
||||
'mb-0', 'mb-1', 'mb-2', 'mb-3', 'mb-4', 'mb-5', 'mb-6', 'mb-8',
|
||||
'ml-0', 'ml-1', 'ml-2', 'ml-3', 'ml-4', 'ml-5', 'ml-6', 'ml-8', 'ml-auto',
|
||||
'mr-0', 'mr-1', 'mr-2', 'mr-3', 'mr-4', 'mr-5', 'mr-6', 'mr-8', 'mr-auto',
|
||||
// Display
|
||||
'block', 'inline-block', 'inline', 'hidden', 'table', 'table-cell', 'table-row',
|
||||
'lg:block', 'lg:inline-block', 'lg:hidden', 'lg:flex',
|
||||
'md:block', 'md:inline-block', 'md:hidden', 'md:flex',
|
||||
'sm:block', 'sm:inline-block', 'sm:hidden', 'sm:flex',
|
||||
// Width/Height
|
||||
'w-full', 'w-auto', 'w-1/2', 'w-1/3', 'w-2/3', 'w-1/4', 'w-3/4',
|
||||
'w-12', 'w-16', 'w-20', 'w-24', 'w-32', 'w-48', 'w-64', 'w-96',
|
||||
'h-full', 'h-screen', 'h-auto', 'h-12', 'h-16', 'h-32', 'h-64',
|
||||
'min-h-screen', 'min-h-full', 'max-w-7xl', 'max-w-full',
|
||||
// Typography
|
||||
'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl',
|
||||
'font-thin', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold',
|
||||
'text-left', 'text-center', 'text-right', 'text-justify',
|
||||
'uppercase', 'lowercase', 'capitalize', 'normal-case',
|
||||
'italic', 'not-italic',
|
||||
'leading-none', 'leading-tight', 'leading-normal', 'leading-loose',
|
||||
// Colors (for dynamic theme)
|
||||
'text-gray-50', 'text-gray-100', 'text-gray-200', 'text-gray-300', 'text-gray-400',
|
||||
'text-gray-500', 'text-gray-600', 'text-gray-700', 'text-gray-800', 'text-gray-900',
|
||||
'bg-white', 'bg-gray-50', 'bg-gray-100', 'bg-gray-200', 'bg-gray-300',
|
||||
'bg-gray-400', 'bg-gray-500', 'bg-gray-600', 'bg-gray-700', 'bg-gray-800', 'bg-gray-900',
|
||||
'bg-transparent',
|
||||
'border-gray-100', 'border-gray-200', 'border-gray-300', 'border-gray-400',
|
||||
'border-gray-500', 'border-gray-600', 'border-gray-700', 'border-gray-800',
|
||||
// Dark mode
|
||||
'dark:bg-gray-700', 'dark:bg-gray-800', 'dark:bg-gray-900',
|
||||
'dark:text-gray-50', 'dark:text-gray-100', 'dark:text-gray-200', 'dark:text-gray-300', 'dark:text-gray-400',
|
||||
'dark:text-white',
|
||||
'dark:border-gray-600', 'dark:border-gray-700', 'dark:border-gray-800',
|
||||
// Borders and Rounding
|
||||
'border', 'border-0', 'border-2', 'border-4', 'border-8',
|
||||
'border-t', 'border-b', 'border-l', 'border-r',
|
||||
'border-t-0', 'border-b-0', 'border-l-0', 'border-r-0',
|
||||
'rounded', 'rounded-sm', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-2xl', 'rounded-full',
|
||||
'rounded-t', 'rounded-b', 'rounded-l', 'rounded-r',
|
||||
'rounded-t-md', 'rounded-b-md', 'rounded-l-md', 'rounded-r-md',
|
||||
// Shadows
|
||||
'shadow-none', 'shadow-sm', 'shadow', 'shadow-md', 'shadow-lg', 'shadow-xl', 'shadow-2xl',
|
||||
// Overflow
|
||||
'overflow-auto', 'overflow-hidden', 'overflow-visible', 'overflow-scroll',
|
||||
'overflow-x-auto', 'overflow-x-hidden', 'overflow-x-visible', 'overflow-x-scroll',
|
||||
'overflow-y-auto', 'overflow-y-hidden', 'overflow-y-visible', 'overflow-y-scroll',
|
||||
// Position
|
||||
'static', 'fixed', 'absolute', 'relative', 'sticky',
|
||||
'top-0', 'right-0', 'bottom-0', 'left-0',
|
||||
'inset-0', 'inset-x-0', 'inset-y-0',
|
||||
// Z-index
|
||||
'z-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50', 'z-auto',
|
||||
// Opacity
|
||||
'opacity-0', 'opacity-25', 'opacity-50', 'opacity-75', 'opacity-100',
|
||||
// Cursor
|
||||
'cursor-auto', 'cursor-pointer', 'cursor-not-allowed', 'cursor-wait',
|
||||
// Forms
|
||||
'form-input', 'form-select', 'form-checkbox', 'form-radio',
|
||||
// Tables
|
||||
'table-auto', 'table-fixed', 'border-collapse', 'border-separate',
|
||||
// Transitions
|
||||
'transition', 'transition-all', 'transition-colors', 'transition-opacity',
|
||||
'duration-75', 'duration-100', 'duration-150', 'duration-200', 'duration-300',
|
||||
'ease-in', 'ease-out', 'ease-in-out', 'ease-linear',
|
||||
// Transform
|
||||
'transform', 'transform-none',
|
||||
'scale-90', 'scale-95', 'scale-100', 'scale-105', 'scale-110',
|
||||
// Visibility
|
||||
'visible', 'invisible',
|
||||
// Quill specific classes
|
||||
'ql-toolbar', 'ql-container', 'ql-editor', 'quill-editor',
|
||||
'ql-snow', 'ql-bubble'
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### lib/tasks/active_admin.rake
|
||||
```ruby
|
||||
namespace :active_admin do
|
||||
desc "Build Active Admin Tailwind stylesheets"
|
||||
task build: :environment do
|
||||
command = [
|
||||
"npx", "tailwindcss",
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-c", Rails.root.join("tailwind.config.js").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command, exception: true)
|
||||
end
|
||||
|
||||
desc "Watch Active Admin Tailwind stylesheets"
|
||||
task watch: :environment do
|
||||
command = [
|
||||
"npx", "tailwindcss",
|
||||
"--watch",
|
||||
"-i", Rails.root.join("app/assets/stylesheets/active_admin.css").to_s,
|
||||
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s,
|
||||
"-c", Rails.root.join("tailwind.config.js").to_s,
|
||||
"-m"
|
||||
]
|
||||
|
||||
system(*command)
|
||||
end
|
||||
end
|
||||
|
||||
Rake::Task["assets:precompile"].enhance(["active_admin:build"])
|
||||
|
||||
Rake::Task["test:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("test:prepare")
|
||||
Rake::Task["spec:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("spec:prepare")
|
||||
Rake::Task["db:test:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("db:test:prepare")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
```ruby
|
||||
# app/admin/posts.rb
|
||||
ActiveAdmin.register Post do
|
||||
permit_params :title, :content, :description
|
||||
|
||||
form do |f|
|
||||
f.inputs do
|
||||
f.input :title
|
||||
f.input :content, as: :quill_editor
|
||||
f.input :description, as: :quill_editor, input_html: {
|
||||
data: {
|
||||
options: {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline'],
|
||||
['link', 'image'],
|
||||
[{ list: 'ordered' }, { list: 'bullet' }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Custom Toolbar Configuration
|
||||
```ruby
|
||||
f.input :content, as: :quill_editor, input_html: {
|
||||
data: {
|
||||
options: {
|
||||
theme: 'snow', # or 'bubble'
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ 'header': [1, 2, 3, false] }],
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
[{ 'script': 'sub' }, { 'script': 'super' }],
|
||||
['blockquote', 'code-block'],
|
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
||||
[{ 'align': [] }],
|
||||
['link', 'image', 'video'],
|
||||
['clean']
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Image Upload Support (Optional)
|
||||
```bash
|
||||
npm install quill-image-uploader
|
||||
```
|
||||
|
||||
Configure in your form:
|
||||
```ruby
|
||||
f.input :content, as: :quill_editor, input_html: {
|
||||
data: {
|
||||
plugins: {
|
||||
image_uploader: {
|
||||
server_url: '/admin/upload',
|
||||
field_name: 'file'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript API
|
||||
|
||||
The gem exposes these global functions:
|
||||
|
||||
```javascript
|
||||
// Get all Quill editor instances
|
||||
const editors = getQuillEditors();
|
||||
|
||||
// Get editor by index (0-based)
|
||||
const firstEditor = getQuillEditorByIndex(0);
|
||||
|
||||
// Get editor by element ID
|
||||
const editor = getQuillEditorByElementId('post_content');
|
||||
|
||||
// Access Quill API directly
|
||||
editor.getContents();
|
||||
editor.setText('New content');
|
||||
editor.format('bold', true);
|
||||
```
|
||||
|
||||
## Migration from Legacy Versions
|
||||
|
||||
### Removing jQuery Dependencies
|
||||
|
||||
**Old initialization (jQuery)**:
|
||||
```javascript
|
||||
$(document).ready(function() {
|
||||
// Initialization code
|
||||
});
|
||||
```
|
||||
|
||||
**New initialization (Vanilla JS)**:
|
||||
```javascript
|
||||
// Automatically handled by the gem
|
||||
// Or manually initialize:
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Custom initialization if needed
|
||||
});
|
||||
```
|
||||
|
||||
### Asset Pipeline Changes
|
||||
|
||||
**Old (Sprockets)**:
|
||||
```javascript
|
||||
//= require activeadmin/quill_editor/quill
|
||||
//= require activeadmin/quill_editor_input
|
||||
```
|
||||
|
||||
**New (Propshaft/esbuild)**:
|
||||
```javascript
|
||||
import Quill from 'quill';
|
||||
// Gem's initialization auto-loads
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Quill not initializing
|
||||
**Solution**: Ensure JavaScript is properly included:
|
||||
- Check that `window.Quill` is defined
|
||||
- Verify the gem's JS is loaded after Quill
|
||||
- Check browser console for errors
|
||||
|
||||
### Issue: Styles not loading
|
||||
**Solution**: Verify CSS imports:
|
||||
- Ensure Quill CSS is imported
|
||||
- Check that Tailwind processes the styles
|
||||
- Verify no CSS conflicts with ActiveAdmin
|
||||
|
||||
### Issue: Form submission not saving content
|
||||
**Solution**: The gem automatically handles form submission. Check:
|
||||
- Hidden input field is present in HTML
|
||||
- No JavaScript errors preventing update
|
||||
- Strong parameters permit the field
|
||||
|
||||
## Testing Your Implementation
|
||||
|
||||
### RSpec System Tests
|
||||
```ruby
|
||||
# spec/system/admin_posts_spec.rb
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Admin Posts', type: :system do
|
||||
it 'creates a post with Quill editor' do
|
||||
visit '/admin/posts/new'
|
||||
|
||||
# Fill in Quill editor
|
||||
within '[data-aa-quill-editor]' do
|
||||
find('.ql-editor').set('Test content')
|
||||
end
|
||||
|
||||
click_button 'Create Post'
|
||||
|
||||
expect(page).to have_content('Post was successfully created')
|
||||
expect(Post.last.content).to include('Test content')
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Version Compatibility Matrix
|
||||
|
||||
| Gem Version | ActiveAdmin | Rails | Ruby | Quill |
|
||||
|-------------|-------------|-------|------|-------|
|
||||
| 2.0.x | 2.9-4.x | 7.0-8.x | 3.2+ | 2.x |
|
||||
| 1.x | 2.x-3.x | 6.1-7.x | 3.0+ | 1.x |
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Quill Documentation](https://quilljs.com/)
|
||||
- [ActiveAdmin 4 Migration Guide](https://github.com/activeadmin/activeadmin/wiki)
|
||||
- [Propshaft Documentation](https://github.com/rails/propshaft)
|
||||
- [Rails 8 Asset Pipeline Guide](https://guides.rubyonrails.org/asset_pipeline.html)
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- GitHub Issues: https://github.com/rs-pro/activeadmin-quill_editor/issues
|
||||
- ActiveAdmin Community: https://github.com/activeadmin/activeadmin/discussions
|
||||
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', rel: 'noopener'
|
||||
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
|
||||
3
extra/.bashrc
Обычный файл
3
extra/.bashrc
Обычный файл
@@ -0,0 +1,3 @@
|
||||
alias ls='ls --color'
|
||||
alias ll='ls -l'
|
||||
alias la='ls -la'
|
||||
7
extra/.env
Обычный файл
7
extra/.env
Обычный файл
@@ -0,0 +1,7 @@
|
||||
COMPOSE_PROJECT_NAME=activeadmin_quill_editor
|
||||
|
||||
BUNDLER_VERSION=2.5.23
|
||||
SERVER_PORT=4000
|
||||
|
||||
UID=1000
|
||||
GID=1000
|
||||
31
extra/Dockerfile
Обычный файл
31
extra/Dockerfile
Обычный файл
@@ -0,0 +1,31 @@
|
||||
ARG RUBY_IMAGE=ruby:3
|
||||
FROM ${RUBY_IMAGE}
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV DEVEL=1
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN apt-get update -qq
|
||||
RUN apt-get install -yqq --no-install-recommends build-essential chromium less libyaml-dev nano netcat-traditional pkg-config
|
||||
|
||||
ARG BUNDLER_VERSION
|
||||
RUN gem install bundler -v ${BUNDLER_VERSION}
|
||||
RUN echo 'gem: --no-document' > /etc/gemrc
|
||||
|
||||
ARG UID
|
||||
RUN useradd -u $UID --shell /bin/bash app
|
||||
|
||||
RUN mkdir -p /home/app && chown -R app:app /home/app
|
||||
|
||||
ARG RAILS_VERSION
|
||||
ENV RAILS_VERSION=$RAILS_VERSION
|
||||
|
||||
ARG ACTIVEADMIN_VERSION
|
||||
ENV ACTIVEADMIN_VERSION=$ACTIVEADMIN_VERSION
|
||||
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN bundle install
|
||||
RUN chown -R app:app /usr/local/bundle
|
||||
|
||||
RUN ln -s /app/extra/.bashrc /home/app/.bashrc
|
||||
6
extra/Dockerfile.dockerignore
Обычный файл
6
extra/Dockerfile.dockerignore
Обычный файл
@@ -0,0 +1,6 @@
|
||||
# Ignore everything but the required files for bundle install
|
||||
/**/*
|
||||
|
||||
!/*.gemspec
|
||||
!/Gemfile
|
||||
!/lib
|
||||
6
extra/dev_setup.sh
Исполняемый файл
6
extra/dev_setup.sh
Исполняемый файл
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
export DEVEL=1
|
||||
|
||||
export RAILS_VERSION=7.2.2.1
|
||||
export ACTIVEADMIN_VERSION=3.3.0
|
||||
50
extra/development.md
Обычный файл
50
extra/development.md
Обычный файл
@@ -0,0 +1,50 @@
|
||||
## Development
|
||||
|
||||
### Dev setup
|
||||
|
||||
There are 2 ways to interact with this project:
|
||||
|
||||
1) Using Docker:
|
||||
|
||||
```sh
|
||||
make up # starts the dev services (optional env vars: RUBY / RAILS / ACTIVEADMIN)
|
||||
make specs # run the tests (after up)
|
||||
make lint # run the linters (after up)
|
||||
make server # run the server (after up)
|
||||
make shell # open a shell (after up)
|
||||
make down # cleanup (after up)
|
||||
|
||||
# Example using specific versions:
|
||||
RUBY=3.2 RAILS=7.1 ACTIVEADMIN=3.2.0 make up
|
||||
```
|
||||
|
||||
2) With a local setup:
|
||||
|
||||
```sh
|
||||
# Dev setup (set the required envs):
|
||||
source extra/dev_setup.sh
|
||||
# Install dependencies:
|
||||
bundle update
|
||||
# Run server (or any command):
|
||||
bin/rails s
|
||||
# To try different versions of Rails/ActiveAdmin edit extra/dev_setup.sh
|
||||
```
|
||||
|
||||
### Update the editor
|
||||
|
||||
- Update the CSS/JS editor assets:
|
||||
|
||||
```sh
|
||||
wget "https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" -O "app/assets/stylesheets/activeadmin/quill_editor/quill.snow.css"
|
||||
wget "https://cdn.jsdelivr.net/npm/quill@2/dist/quill.bubble.css" -O "app/assets/stylesheets/activeadmin/quill_editor/quill.bubble.css"
|
||||
wget "https://cdn.jsdelivr.net/npm/quill@2/dist/quill.core.css" -O "app/assets/stylesheets/activeadmin/quill_editor/quill.core.css"
|
||||
|
||||
wget "https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js" -O "app/assets/javascripts/activeadmin/quill_editor/quill.js"
|
||||
wget "https://cdn.jsdelivr.net/npm/quill@2/dist/quill.core.js" -O "app/assets/javascripts/activeadmin/quill_editor/quill.core.js"
|
||||
|
||||
wget "https://github.com/valentsea/quill2-image-uploader/raw/refs/heads/master/dist/quill.imageUploader.min.js" -O "app/assets/javascripts/activeadmin/quill.imageUploader.min.js"
|
||||
|
||||
wget "https://github.com/valentsea/quill2-image-uploader/raw/refs/heads/master/dist/quill.imageUploader.min.css" -O "app/assets/stylesheets/activeadmin/quill.imageUploader.min.css"
|
||||
```
|
||||
|
||||
- Check the changes, most of them should be for updated files plus some new / removed file
|
||||
19
extra/docker-compose.yml
Обычный файл
19
extra/docker-compose.yml
Обычный файл
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: extra/Dockerfile
|
||||
args:
|
||||
BUNDLER_VERSION: ${BUNDLER_VERSION}
|
||||
RUBY_IMAGE: ruby:${RUBY:-3.4}-slim
|
||||
RAILS_VERSION: ${RAILS:-}
|
||||
ACTIVEADMIN_VERSION: ${ACTIVEADMIN:-}
|
||||
UID: ${UID}
|
||||
user: ${UID}:${GID}
|
||||
ports:
|
||||
- ${SERVER_PORT}:${SERVER_PORT}
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ..:/app
|
||||
stdin_open: true
|
||||
tty: true
|
||||
Двоичные данные
extra/screenshot.png
Обычный файл
Двоичные данные
extra/screenshot.png
Обычный файл
Двоичный файл не отображается.
|
После Ширина: | Высота: | Размер: 52 KiB |
2
gemfiles/.bundle/config
Обычный файл
2
gemfiles/.bundle/config
Обычный файл
@@ -0,0 +1,2 @@
|
||||
---
|
||||
BUNDLE_RETRY: "1"
|
||||
32
gemfiles/rails_7.2_active_admin_4.x.gemfile
Обычный файл
32
gemfiles/rails_7.2_active_admin_4.x.gemfile
Обычный файл
@@ -0,0 +1,32 @@
|
||||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "rails", "~> 7.2.0"
|
||||
gem "activeadmin", "4.0.0.beta20"
|
||||
gem "sqlite3"
|
||||
gem "concurrent-ruby", "1.3.4"
|
||||
gem "bigdecimal"
|
||||
gem "csv"
|
||||
gem "mutex_m"
|
||||
gem "puma"
|
||||
gem "importmap-rails"
|
||||
gem "propshaft"
|
||||
gem "capybara"
|
||||
gem "cuprite"
|
||||
gem "rspec_junit_formatter"
|
||||
gem "rspec-rails"
|
||||
gem "simplecov", require: false
|
||||
gem "super_diff"
|
||||
gem "fasterer"
|
||||
gem "rubocop"
|
||||
gem "rubocop-capybara"
|
||||
gem "rubocop-packaging"
|
||||
gem "rubocop-performance"
|
||||
gem "rubocop-rails"
|
||||
gem "rubocop-rspec"
|
||||
gem "rubocop-rspec_rails"
|
||||
gem "appraisal"
|
||||
gem "pry-rails"
|
||||
|
||||
gemspec path: "../"
|
||||
32
gemfiles/rails_8.0_active_admin_4.x.gemfile
Обычный файл
32
gemfiles/rails_8.0_active_admin_4.x.gemfile
Обычный файл
@@ -0,0 +1,32 @@
|
||||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "rails", "~> 8.0.0"
|
||||
gem "activeadmin", "4.0.0.beta20"
|
||||
gem "sqlite3"
|
||||
gem "concurrent-ruby", "1.3.4"
|
||||
gem "bigdecimal"
|
||||
gem "csv"
|
||||
gem "mutex_m"
|
||||
gem "puma"
|
||||
gem "importmap-rails"
|
||||
gem "propshaft"
|
||||
gem "capybara"
|
||||
gem "cuprite"
|
||||
gem "rspec_junit_formatter"
|
||||
gem "rspec-rails"
|
||||
gem "simplecov", require: false
|
||||
gem "super_diff"
|
||||
gem "fasterer"
|
||||
gem "rubocop"
|
||||
gem "rubocop-capybara"
|
||||
gem "rubocop-packaging"
|
||||
gem "rubocop-performance"
|
||||
gem "rubocop-rails"
|
||||
gem "rubocop-rspec"
|
||||
gem "rubocop-rspec_rails"
|
||||
gem "appraisal"
|
||||
gem "pry-rails"
|
||||
|
||||
gemspec path: "../"
|
||||
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')
|
||||
@@ -1 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'activeadmin/quill_editor/engine'
|
||||
require 'formtastic/inputs/quill_editor_input'
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'active_admin'
|
||||
|
||||
module ActiveAdmin
|
||||
module QuillEditor
|
||||
class Engine < ::Rails::Engine
|
||||
engine_name 'activeadmin_quill_editor'
|
||||
|
||||
# Propshaft automatically includes vendor/assets and app/assets paths
|
||||
# No need for explicit asset path configuration with Propshaft
|
||||
|
||||
initializer 'activeadmin_quill_editor.assets' do |app|
|
||||
# Add assets to precompile list for both Propshaft and Sprockets
|
||||
if app.config.respond_to?(:assets)
|
||||
app.config.assets.precompile += %w[
|
||||
activeadmin/quill_editor.js
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
initializer 'activeadmin_quill_editor.formtastic' do
|
||||
ActiveSupport.on_load :active_admin do
|
||||
require 'formtastic/inputs/quill_editor_input'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ActiveAdmin
|
||||
module QuillEditor
|
||||
VERSION = '0.1.1'
|
||||
VERSION = '2.1.0'
|
||||
QUILL_VERSION = '2.0.3'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'activeadmin/quill_editor'
|
||||
|
||||
require 'formtastic/inputs/quill_editor_input'
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Formtastic
|
||||
module Inputs
|
||||
class QuillEditorInput < Formtastic::Inputs::TextInput
|
||||
def to_html
|
||||
input_wrapping do
|
||||
label_html <<
|
||||
template.content_tag( :div, input_html_options.merge( class: 'quill-editor' ) ) do
|
||||
builder.hidden_field( input_name ) <<
|
||||
template.content_tag( :div, class: 'quill-editor-content' ) do
|
||||
object.send( method ).try :html_safe
|
||||
template.content_tag(:div, input_html_options.merge('data-aa-quill-editor': '1')) do
|
||||
builder.hidden_field(input_name) <<
|
||||
template.content_tag(:div, 'data-aa-quill-content': '1') do
|
||||
object.send(method).try :html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
18
package.json
Обычный файл
18
package.json
Обычный файл
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "activeadmin_quill_editor",
|
||||
"version": "2.0.0",
|
||||
"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"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.22.0",
|
||||
"eslint": "^9.22.0",
|
||||
"globals": "^16.0.0"
|
||||
}
|
||||
}
|
||||
Двоичные данные
screenshot.jpg
Двоичные данные
screenshot.jpg
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 33 KiB |
6
spec/dummy/Rakefile
Обычный файл
6
spec/dummy/Rakefile
Обычный файл
@@ -0,0 +1,6 @@
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require_relative 'config/application'
|
||||
|
||||
Rails.application.load_tasks
|
||||
57
spec/dummy/app/admin/authors.rb
Обычный файл
57
spec/dummy/app/admin/authors.rb
Обычный файл
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ActiveAdmin.register Author do
|
||||
permit_params :name,
|
||||
:email,
|
||||
:age,
|
||||
:avatar,
|
||||
profile_attributes: %i[id description _destroy],
|
||||
posts_attributes: %i[id title description]
|
||||
|
||||
index do
|
||||
selectable_column
|
||||
id_column
|
||||
column :name
|
||||
column :email
|
||||
column :created_at
|
||||
actions
|
||||
end
|
||||
|
||||
filter :name
|
||||
filter :created_at
|
||||
|
||||
show do
|
||||
attributes_table do
|
||||
row :name
|
||||
row :email
|
||||
row :age
|
||||
row :avatar do |record|
|
||||
image_tag url_for(record.avatar), style: 'max-width:800px;max-height:500px' if record.avatar.attached?
|
||||
end
|
||||
row :created_at
|
||||
row :updated_at
|
||||
row :profile
|
||||
row :posts
|
||||
end
|
||||
active_admin_comments_for(resource) if active_admin_config.comments?
|
||||
end
|
||||
|
||||
form do |f|
|
||||
f.inputs do
|
||||
f.input :name
|
||||
f.input :email
|
||||
f.input :age
|
||||
f.input :avatar,
|
||||
as: :file,
|
||||
hint: (object.avatar.attached? ? "Current: #{object.avatar.filename}" : nil)
|
||||
end
|
||||
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
|
||||
32
spec/dummy/app/admin/dashboard.rb
Обычный файл
32
spec/dummy/app/admin/dashboard.rb
Обычный файл
@@ -0,0 +1,32 @@
|
||||
ActiveAdmin.register_page "Dashboard" do
|
||||
menu priority: 1, label: proc { I18n.t("active_admin.dashboard") }
|
||||
|
||||
content title: proc { I18n.t("active_admin.dashboard") } do
|
||||
div class: "blank_slate_container", id: "dashboard_default_message" do
|
||||
span class: "blank_slate" do
|
||||
span I18n.t("active_admin.dashboard_welcome.welcome")
|
||||
small I18n.t("active_admin.dashboard_welcome.call_to_action")
|
||||
end
|
||||
end
|
||||
|
||||
# Here is an example of a simple dashboard with columns and panels.
|
||||
#
|
||||
# columns do
|
||||
# column do
|
||||
# panel "Recent Posts" do
|
||||
# ul do
|
||||
# Post.recent(5).map do |post|
|
||||
# li link_to(post.title, admin_post_path(post))
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# column do
|
||||
# panel "Info" do
|
||||
# para "Welcome to ActiveAdmin."
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
end # content
|
||||
end
|
||||
71
spec/dummy/app/admin/posts.rb
Обычный файл
71
spec/dummy/app/admin/posts.rb
Обычный файл
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ActiveAdmin.register Post do
|
||||
permit_params :author_id, :title, :summary, :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 :summary
|
||||
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_for(resource) if active_admin_config.comments?
|
||||
end
|
||||
|
||||
form do |f|
|
||||
toolbar = %w[bold italic underline link]
|
||||
f.inputs 'Post' do
|
||||
f.input :author
|
||||
f.input :title
|
||||
f.input :summary, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: toolbar } } } }
|
||||
if object.new_record?
|
||||
f.input :description, as: :quill_editor # using default options
|
||||
else
|
||||
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
|
||||
5
spec/dummy/app/admin/profiles.rb
Обычный файл
5
spec/dummy/app/admin/profiles.rb
Обычный файл
@@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ActiveAdmin.register Profile do
|
||||
permit_params :author_id, :description
|
||||
end
|
||||
5
spec/dummy/app/admin/tags.rb
Обычный файл
5
spec/dummy/app/admin/tags.rb
Обычный файл
@@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ActiveAdmin.register Tag do
|
||||
permit_params :name
|
||||
end
|
||||
16
spec/dummy/app/assets/builds/active_admin.css
Обычный файл
16
spec/dummy/app/assets/builds/active_admin.css
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
1
spec/dummy/app/assets/builds/active_admin.css.map
Обычный файл
1
spec/dummy/app/assets/builds/active_admin.css.map
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
17146
spec/dummy/app/assets/builds/active_admin.js
Обычный файл
17146
spec/dummy/app/assets/builds/active_admin.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
7
spec/dummy/app/assets/builds/active_admin.js.map
Обычный файл
7
spec/dummy/app/assets/builds/active_admin.js.map
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
7
spec/dummy/app/assets/config/manifest.js
Обычный файл
7
spec/dummy/app/assets/config/manifest.js
Обычный файл
@@ -0,0 +1,7 @@
|
||||
//= link_tree ../images
|
||||
//= link_directory ../javascripts .js
|
||||
//= link active_admin.css
|
||||
//= link active_admin.js
|
||||
//= link activeadmin_quill_editor.js
|
||||
//= link application.css
|
||||
// OFF link active_storage_db_manifest.js
|
||||
0
spec/dummy/app/assets/images/.keep
Обычный файл
0
spec/dummy/app/assets/images/.keep
Обычный файл
6
spec/dummy/app/assets/javascripts/active_admin_legacy.js.bak
Обычный файл
6
spec/dummy/app/assets/javascripts/active_admin_legacy.js.bak
Обычный файл
@@ -0,0 +1,6 @@
|
||||
//= require active_admin/base
|
||||
|
||||
//= require activeadmin/quill_editor/quill
|
||||
//= require activeadmin/quill_editor_input
|
||||
|
||||
//= require activeadmin/quill.imageUploader.min
|
||||
2
spec/dummy/app/assets/javascripts/application.js
Обычный файл
2
spec/dummy/app/assets/javascripts/application.js
Обычный файл
@@ -0,0 +1,2 @@
|
||||
//= require_tree ../builds
|
||||
//= require activeadmin_quill_editor
|
||||
73
spec/dummy/app/assets/stylesheets/active_admin.css
Обычный файл
73
spec/dummy/app/assets/stylesheets/active_admin.css
Обычный файл
@@ -0,0 +1,73 @@
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
/* Import Quill styles */
|
||||
@import "../../../node_modules/quill/dist/quill.snow.css";
|
||||
@import "../../../node_modules/quill/dist/quill.bubble.css";
|
||||
|
||||
/* Custom Quill integration styles for ActiveAdmin */
|
||||
@layer components {
|
||||
.quill-editor {
|
||||
@apply border border-gray-300 rounded-md;
|
||||
}
|
||||
|
||||
.ql-toolbar {
|
||||
@apply border-b-0 rounded-t-md;
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
@apply border border-gray-300 rounded-b-md;
|
||||
}
|
||||
|
||||
.ql-editor {
|
||||
@apply min-h-[200px];
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
.dark .quill-editor {
|
||||
@apply border-gray-600;
|
||||
}
|
||||
|
||||
.dark .ql-toolbar {
|
||||
@apply bg-gray-800 border-gray-600;
|
||||
}
|
||||
|
||||
.dark .ql-container {
|
||||
@apply bg-gray-900 border-gray-600;
|
||||
}
|
||||
|
||||
.dark .ql-editor {
|
||||
@apply text-gray-100;
|
||||
}
|
||||
|
||||
/* Fix toolbar button styles */
|
||||
.ql-toolbar button:hover {
|
||||
@apply bg-gray-100 dark:bg-gray-700;
|
||||
}
|
||||
|
||||
/* Ensure icons are visible in dark mode */
|
||||
.dark .ql-toolbar button svg {
|
||||
@apply text-gray-300;
|
||||
}
|
||||
|
||||
.dark .ql-toolbar button:hover svg {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.dark .ql-toolbar .ql-stroke {
|
||||
stroke: #9ca3af !important;
|
||||
}
|
||||
|
||||
.dark .ql-toolbar .ql-fill {
|
||||
fill: #9ca3af !important;
|
||||
}
|
||||
|
||||
.dark .ql-toolbar button:hover .ql-stroke {
|
||||
stroke: white !important;
|
||||
}
|
||||
|
||||
.dark .ql-toolbar button:hover .ql-fill {
|
||||
fill: white !important;
|
||||
}
|
||||
}
|
||||
22
spec/dummy/app/assets/stylesheets/active_admin_legacy.scss.bak
Обычный файл
22
spec/dummy/app/assets/stylesheets/active_admin_legacy.scss.bak
Обычный файл
@@ -0,0 +1,22 @@
|
||||
// SASS variable overrides must be declared before loading up Active Admin's styles.
|
||||
//
|
||||
// To view the variables that Active Admin provides, take a look at
|
||||
// `app/assets/stylesheets/active_admin/mixins/_variables.scss` in the
|
||||
// Active Admin source.
|
||||
//
|
||||
// For example, to change the sidebar width:
|
||||
// $sidebar-width: 242px;
|
||||
|
||||
// Active Admin's got SASS!
|
||||
@import 'active_admin/mixins';
|
||||
@import 'active_admin/base';
|
||||
|
||||
@import 'activeadmin/quill_editor/quill.snow';
|
||||
@import 'activeadmin/quill_editor_input';
|
||||
|
||||
@import 'activeadmin/quill.imageUploader.min';
|
||||
|
||||
// Overriding any non-variable SASS must be done after the fact.
|
||||
// For example, to change the default status-tag color:
|
||||
//
|
||||
// .status_tag { background: #6090DB; }
|
||||
15
spec/dummy/app/assets/stylesheets/application.css
Обычный файл
15
spec/dummy/app/assets/stylesheets/application.css
Обычный файл
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||
* listed below.
|
||||
*
|
||||
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||
*
|
||||
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
||||
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||
* It is generally better to create a new file per style scope.
|
||||
*
|
||||
*= require_tree .
|
||||
*= require_self
|
||||
*/
|
||||
4
spec/dummy/app/channels/application_cable/channel.rb
Обычный файл
4
spec/dummy/app/channels/application_cable/channel.rb
Обычный файл
@@ -0,0 +1,4 @@
|
||||
module ApplicationCable
|
||||
class Channel < ActionCable::Channel::Base
|
||||
end
|
||||
end
|
||||
4
spec/dummy/app/channels/application_cable/connection.rb
Обычный файл
4
spec/dummy/app/channels/application_cable/connection.rb
Обычный файл
@@ -0,0 +1,4 @@
|
||||
module ApplicationCable
|
||||
class Connection < ActionCable::Connection::Base
|
||||
end
|
||||
end
|
||||
2
spec/dummy/app/controllers/application_controller.rb
Обычный файл
2
spec/dummy/app/controllers/application_controller.rb
Обычный файл
@@ -0,0 +1,2 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
end
|
||||
0
spec/dummy/app/controllers/concerns/.keep
Обычный файл
0
spec/dummy/app/controllers/concerns/.keep
Обычный файл
2
spec/dummy/app/helpers/application_helper.rb
Обычный файл
2
spec/dummy/app/helpers/application_helper.rb
Обычный файл
@@ -0,0 +1,2 @@
|
||||
module ApplicationHelper
|
||||
end
|
||||
26
spec/dummy/app/javascript/active_admin.js
Обычный файл
26
spec/dummy/app/javascript/active_admin.js
Обычный файл
@@ -0,0 +1,26 @@
|
||||
// Import ActiveAdmin - this already includes all features and Rails UJS
|
||||
// DO NOT import Rails separately as it's already included and started in ActiveAdmin
|
||||
import '@activeadmin/activeadmin';
|
||||
|
||||
// Import Quill from NPM and make it globally available
|
||||
import Quill from 'quill';
|
||||
window.Quill = Quill;
|
||||
|
||||
// Import the image uploader plugin if available (optional)
|
||||
try {
|
||||
const ImageUploader = require('quill-image-uploader');
|
||||
window.ImageUploader = ImageUploader.default || ImageUploader;
|
||||
} catch(e) {
|
||||
// Image uploader is optional (silent failure)
|
||||
}
|
||||
|
||||
// Import the Quill Editor initialization module
|
||||
import QuillEditorModule from 'activeadmin/quill_editor';
|
||||
|
||||
// Now that Quill is available, initialize the editors
|
||||
// This ensures proper initialization order without setTimeout hacks
|
||||
if (window.QuillEditor && window.QuillEditor.init) {
|
||||
window.QuillEditor.init();
|
||||
} else if (QuillEditorModule && QuillEditorModule.init) {
|
||||
QuillEditorModule.init();
|
||||
}
|
||||
15
spec/dummy/app/javascript/packs/application.js
Обычный файл
15
spec/dummy/app/javascript/packs/application.js
Обычный файл
@@ -0,0 +1,15 @@
|
||||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||
// listed below.
|
||||
//
|
||||
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
||||
//
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
||||
//
|
||||
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require rails-ujs
|
||||
//= require activestorage
|
||||
//= require_tree .
|
||||
7
spec/dummy/app/jobs/application_job.rb
Обычный файл
7
spec/dummy/app/jobs/application_job.rb
Обычный файл
@@ -0,0 +1,7 @@
|
||||
class ApplicationJob < ActiveJob::Base
|
||||
# Automatically retry jobs that encountered a deadlock
|
||||
# retry_on ActiveRecord::Deadlocked
|
||||
|
||||
# Most jobs are safe to ignore if the underlying records are no longer available
|
||||
# discard_on ActiveJob::DeserializationError
|
||||
end
|
||||
4
spec/dummy/app/mailers/application_mailer.rb
Обычный файл
4
spec/dummy/app/mailers/application_mailer.rb
Обычный файл
@@ -0,0 +1,4 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: 'from@example.com'
|
||||
layout 'mailer'
|
||||
end
|
||||
7
spec/dummy/app/models/application_record.rb
Обычный файл
7
spec/dummy/app/models/application_record.rb
Обычный файл
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
|
||||
scope :published, -> {}
|
||||
end
|
||||
32
spec/dummy/app/models/author.rb
Обычный файл
32
spec/dummy/app/models/author.rb
Обычный файл
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Author < ApplicationRecord
|
||||
has_many :posts
|
||||
has_many :published_posts, -> { published }, class_name: 'Post'
|
||||
has_many :recent_posts, -> { recents }, class_name: 'Post'
|
||||
|
||||
has_many :tags, through: :posts
|
||||
|
||||
has_one :profile, inverse_of: :author, dependent: :destroy
|
||||
|
||||
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' }
|
||||
|
||||
validate -> {
|
||||
errors.add( :base, 'Invalid age' ) if !age || age.to_i % 3 == 1
|
||||
}
|
||||
|
||||
def to_s
|
||||
"#{name} (#{age})"
|
||||
end
|
||||
|
||||
class << self
|
||||
def ransackable_attributes(_auth_object = nil)
|
||||
%w[age created_at email id name updated_at]
|
||||
end
|
||||
end
|
||||
end
|
||||
0
spec/dummy/app/models/concerns/.keep
Обычный файл
0
spec/dummy/app/models/concerns/.keep
Обычный файл
37
spec/dummy/app/models/post.rb
Обычный файл
37
spec/dummy/app/models/post.rb
Обычный файл
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Post < ApplicationRecord
|
||||
belongs_to :author, inverse_of: :posts, autosave: true
|
||||
|
||||
has_one :author_profile, through: :author, source: :profile
|
||||
|
||||
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) }
|
||||
scope :recents, -> { where('created_at > ?', Date.today - 8.month) }
|
||||
|
||||
def short_title
|
||||
title.truncate 10
|
||||
end
|
||||
|
||||
def upper_title
|
||||
title.upcase
|
||||
end
|
||||
|
||||
class << self
|
||||
def ransackable_associations(_auth_object = nil)
|
||||
%w[author author_profile post_tags tags images_attachments images_blobs]
|
||||
end
|
||||
|
||||
def ransackable_attributes(_auth_object = nil)
|
||||
%w[author_id category created_at description dt id position published title summary updated_at]
|
||||
end
|
||||
end
|
||||
end
|
||||
15
spec/dummy/app/models/post_tag.rb
Обычный файл
15
spec/dummy/app/models/post_tag.rb
Обычный файл
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PostTag < ApplicationRecord
|
||||
belongs_to :post, inverse_of: :post_tags
|
||||
belongs_to :tag, inverse_of: :post_tags
|
||||
|
||||
validates :post, presence: true
|
||||
validates :tag, presence: true
|
||||
|
||||
class << self
|
||||
def ransackable_attributes(auth_object = nil)
|
||||
%w[created_at id post_id tag_id updated_at]
|
||||
end
|
||||
end
|
||||
end
|
||||
19
spec/dummy/app/models/profile.rb
Обычный файл
19
spec/dummy/app/models/profile.rb
Обычный файл
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Profile < ApplicationRecord
|
||||
belongs_to :author, inverse_of: :profile, touch: true
|
||||
|
||||
def to_s
|
||||
description
|
||||
end
|
||||
|
||||
class << self
|
||||
def ransackable_associations(_auth_object = nil)
|
||||
%w[author]
|
||||
end
|
||||
|
||||
def ransackable_attributes(_auth_object = nil)
|
||||
%w[author_id created_at description id updated_at]
|
||||
end
|
||||
end
|
||||
end
|
||||
16
spec/dummy/app/models/tag.rb
Обычный файл
16
spec/dummy/app/models/tag.rb
Обычный файл
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Tag < ApplicationRecord
|
||||
has_many :post_tags, inverse_of: :tag, dependent: :destroy
|
||||
has_many :posts, through: :post_tags
|
||||
|
||||
class << self
|
||||
def ransackable_associations(auth_object = nil)
|
||||
%w[post_tags posts]
|
||||
end
|
||||
|
||||
def ransackable_attributes(auth_object = nil)
|
||||
%w[created_at id id_value name updated_at]
|
||||
end
|
||||
end
|
||||
end
|
||||
15
spec/dummy/app/views/layouts/application.html.erb
Обычный файл
15
spec/dummy/app/views/layouts/application.html.erb
Обычный файл
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Dummy</title>
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<%= stylesheet_link_tag 'application', media: 'all' %>
|
||||
<%= javascript_include_tag 'application', 'data-turbo-track': 'reload', defer: true %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
13
spec/dummy/app/views/layouts/mailer.html.erb
Обычный файл
13
spec/dummy/app/views/layouts/mailer.html.erb
Обычный файл
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Email styles need to be inline */
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
1
spec/dummy/app/views/layouts/mailer.text.erb
Обычный файл
1
spec/dummy/app/views/layouts/mailer.text.erb
Обычный файл
@@ -0,0 +1 @@
|
||||
<%= yield %>
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user