1 Коммитов
main ... v0.1.4

Автор SHA1 Сообщение Дата
mat@blocknot.es
1f13f6c65a v0.1.4: PR4 + Quill Editor 1.3.6 2018-11-03 17:45:21 +01:00
163 изменённых файлов: 22428 добавлений и 25303 удалений

Просмотреть файл

@@ -1,7 +0,0 @@
---
exclude_paths:
- bin/*
- db/schema.rb
- gemfiles/**/*
- spec/dummy/**/*
- vendor/**/*

13
.github/FUNDING.yml поставляемый
Просмотреть файл

@@ -1,13 +0,0 @@
# 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 поставляемый
Просмотреть файл

@@ -1,84 +0,0 @@
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 поставляемый
Просмотреть файл

@@ -1,17 +1,3 @@
*.gem
_misc/
*.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

Просмотреть файл

@@ -1,13 +0,0 @@
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

Просмотреть файл

@@ -1,8 +0,0 @@
---
runner:
fasterer:
cmd: bin/fasterer
level: info
rubocop:
cmd: bin/rubocop
level: info

2
.rspec
Просмотреть файл

@@ -1,2 +0,0 @@
--require rails_helper
--format documentation

Просмотреть файл

@@ -1,70 +0,0 @@
---
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

Просмотреть файл

@@ -1,19 +0,0 @@
# 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.

Просмотреть файл

@@ -1,19 +0,0 @@
# 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

Просмотреть файл

@@ -1,72 +0,0 @@
# 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
Просмотреть файл

@@ -1,92 +1,4 @@
# frozen_string_literal: true
source 'https://rubygems.org'
def eval_version(dependency, version)
return [dependency] if version.empty?
gemspec
# 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-2022 Mattia Roccoberton
Copyright (c) 2017 Mattia Roccoberton
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

Просмотреть файл

@@ -1,47 +0,0 @@
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
Просмотреть файл

@@ -1,59 +1,33 @@
# ActiveAdmin Quill Editor
[![gem version](https://badge.fury.io/rb/activeadmin_quill_editor.svg)](https://badge.fury.io/rb/activeadmin_quill_editor)
[![gem downloads](https://badgen.net/rubygems/dt/activeadmin_quill_editor)](https://rubygems.org/gems/activeadmin_quill_editor)
[![CI](https://github.com/blocknotes/activeadmin_quill_editor/actions/workflows/ci.yml/badge.svg)](https://github.com/blocknotes/activeadmin_quill_editor/actions/workflows/ci.yml)
# ActiveAdmin Quill Editor [![Gem Version](https://badge.fury.io/rb/activeadmin_quill_editor.svg)](https://badge.fury.io/rb/activeadmin_quill_editor)
An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill) in form fields.
An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill)
![screenshot](extra/screenshot.png)
Please :star: if you like it.
![screenshot](screenshot.jpg)
## Install
_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';
- 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
@import 'activeadmin/quill_editor_input';
```
- Add at the end of your Active Admin javascripts (_app/assets/javascripts/active_admin.js_):
- Add at the end of your ActiveAdmin 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
If you installed Active Admin with Webpacker support:
Why 2 separated scripts? In this way you can include a different version of *quill editor* if you like.
- 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')
```
## Options
## 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.
**data-options**: permits to set *quill editor* options directly - see [options list](https://quilljs.com/docs/quickstart/)
## Examples
### Basic usage
```ruby
# Active Admin article form conf:
# ActiveAdmin article form conf:
form do |f|
f.inputs 'Article' do
f.input :title
@@ -64,59 +38,26 @@ If you are using Action Text (`has_rich_text`) on the same field of the Quill ed
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'}}}
```
### ImageUploader plugin
## Notes
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).
- Upload functions (Images, Documents, Files, etc.) are not implemented yet
## Do you like it? Star it!
If you use this component just star it. A developer is more motivated to improve a project when there is some interest. My other [Active Admin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source).
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
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
## License
The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
[MIT](LICENSE.txt)

Просмотреть файл

@@ -1,27 +1,3 @@
# frozen_string_literal: true
# encoding: utf-8
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
require "bundler/gem_tasks"

Просмотреть файл

@@ -1,30 +0,0 @@
# 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

19
activeadmin_quill_editor.gemspec Обычный файл
Просмотреть файл

@@ -0,0 +1,19 @@
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

Просмотреть файл

@@ -1,362 +0,0 @@
/******/ (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
/***/ })
/******/ ]);

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

8
app/assets/javascripts/activeadmin/quill_editor/quill.min.js поставляемый Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@@ -0,0 +1,49 @@
window.onload = function () {
initQuillEditors();
}
$(document).on('has_many_add:after', function() {
initQuillEditors();
});
var initQuillEditors = 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' );
var isActive = editors[i].classList.contains('quill-editor--active');
if( content && !isActive) {
var options = editors[i].getAttribute( 'data-options' ) ? JSON.parse( editors[i].getAttribute( 'data-options' ) ) : default_options;
editors[i]['_quill-editor'] = new Quill( content, options );
editors[i].classList += ' quill-editor--active';
}
}
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;
}
};
}
};

Просмотреть файл

@@ -0,0 +1,24 @@
@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

Просмотреть файл

@@ -1,56 +0,0 @@
// 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;
}
}

Просмотреть файл

@@ -1,33 +0,0 @@
.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);
}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@@ -1,29 +0,0 @@
#!/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")

Просмотреть файл

@@ -1,16 +0,0 @@
#!/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'

Просмотреть файл

@@ -1,29 +0,0 @@
#!/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")

Просмотреть файл

@@ -1,29 +0,0 @@
#!/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")

Просмотреть файл

@@ -1,29 +0,0 @@
#!/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")

Просмотреть файл

@@ -1,169 +0,0 @@
# 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

Просмотреть файл

@@ -1,371 +0,0 @@
# 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.

Просмотреть файл

@@ -1,136 +0,0 @@
# 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.

Просмотреть файл

@@ -1,141 +0,0 @@
# 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

Просмотреть файл

@@ -1,187 +0,0 @@
# 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

Просмотреть файл

@@ -1,525 +0,0 @@
# 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

Просмотреть файл

@@ -1,73 +0,0 @@
# 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

Просмотреть файл

@@ -1,6 +0,0 @@
//= require active_admin/base
//= require activeadmin/quill_editor/quill
//= require activeadmin/quill_editor_input
//= require activeadmin/quill.imageUploader.min

Просмотреть файл

@@ -1,7 +0,0 @@
@import 'active_admin/mixins';
@import 'active_admin/base';
@import 'activeadmin/quill_editor/quill.snow';
@import 'activeadmin/quill_editor_input';
@import 'activeadmin/quill.imageUploader.min';

Просмотреть файл

@@ -1,7 +0,0 @@
# frozen_string_literal: true
class Post < ApplicationRecord
has_many_attached :images
validates :title, allow_blank: false, presence: true
end

Просмотреть файл

@@ -1,3 +0,0 @@
alias ls='ls --color'
alias ll='ls -l'
alias la='ls -la'

Просмотреть файл

@@ -1,7 +0,0 @@
COMPOSE_PROJECT_NAME=activeadmin_quill_editor
BUNDLER_VERSION=2.5.23
SERVER_PORT=4000
UID=1000
GID=1000

Просмотреть файл

@@ -1,31 +0,0 @@
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

Просмотреть файл

@@ -1,6 +0,0 @@
# Ignore everything but the required files for bundle install
/**/*
!/*.gemspec
!/Gemfile
!/lib

Просмотреть файл

@@ -1,6 +0,0 @@
#!/bin/sh
export DEVEL=1
export RAILS_VERSION=7.2.2.1
export ACTIVEADMIN_VERSION=3.3.0

Просмотреть файл

@@ -1,50 +0,0 @@
## 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

Просмотреть файл

@@ -1,19 +0,0 @@
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

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 52 KiB

Просмотреть файл

@@ -1,2 +0,0 @@
---
BUNDLE_RETRY: "1"

Просмотреть файл

@@ -1,32 +0,0 @@
# 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: "../"

Просмотреть файл

@@ -1,32 +0,0 @@
# 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: "../"

Просмотреть файл

@@ -1,7 +0,0 @@
// 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,4 +1 @@
# frozen_string_literal: true
require 'activeadmin/quill_editor/engine'
require 'formtastic/inputs/quill_editor_input'

Просмотреть файл

@@ -1,29 +1,9 @@
# 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,8 +1,5 @@
# frozen_string_literal: true
module ActiveAdmin
module QuillEditor
VERSION = '2.1.0'
QUILL_VERSION = '2.0.3'
VERSION = '0.1.4'
end
end

Просмотреть файл

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'activeadmin/quill_editor'
require 'formtastic/inputs/quill_editor_input'

Просмотреть файл

@@ -1,17 +1,15 @@
# 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('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
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
end
end
end
end
end

Просмотреть файл

@@ -1,18 +0,0 @@
{
"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 Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 33 KiB

Просмотреть файл

@@ -1,6 +0,0 @@
# 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

Просмотреть файл

@@ -1,57 +0,0 @@
# 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

Просмотреть файл

@@ -1,32 +0,0 @@
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

Просмотреть файл

@@ -1,71 +0,0 @@
# 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

Просмотреть файл

@@ -1,5 +0,0 @@
# frozen_string_literal: true
ActiveAdmin.register Profile do
permit_params :author_id, :description
end

Просмотреть файл

@@ -1,5 +0,0 @@
# frozen_string_literal: true
ActiveAdmin.register Tag do
permit_params :name
end

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@@ -1,7 +0,0 @@
//= 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

Просмотреть файл

Просмотреть файл

@@ -1,6 +0,0 @@
//= require active_admin/base
//= require activeadmin/quill_editor/quill
//= require activeadmin/quill_editor_input
//= require activeadmin/quill.imageUploader.min

Просмотреть файл

@@ -1,2 +0,0 @@
//= require_tree ../builds
//= require activeadmin_quill_editor

Просмотреть файл

@@ -1,73 +0,0 @@
@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;
}
}

Просмотреть файл

@@ -1,22 +0,0 @@
// 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; }

Просмотреть файл

@@ -1,15 +0,0 @@
/*
* 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
*/

Просмотреть файл

@@ -1,4 +0,0 @@
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end

Просмотреть файл

@@ -1,4 +0,0 @@
module ApplicationCable
class Connection < ActionCable::Connection::Base
end
end

Просмотреть файл

@@ -1,2 +0,0 @@
class ApplicationController < ActionController::Base
end

Просмотреть файл

Просмотреть файл

@@ -1,2 +0,0 @@
module ApplicationHelper
end

Просмотреть файл

@@ -1,26 +0,0 @@
// 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();
}

Просмотреть файл

@@ -1,15 +0,0 @@
// 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 .

Просмотреть файл

@@ -1,7 +0,0 @@
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

Просмотреть файл

@@ -1,4 +0,0 @@
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
end

Просмотреть файл

@@ -1,7 +0,0 @@
# frozen_string_literal: true
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
scope :published, -> {}
end

Просмотреть файл

@@ -1,32 +0,0 @@
# 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

Просмотреть файл

Просмотреть файл

@@ -1,37 +0,0 @@
# 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

Просмотреть файл

@@ -1,15 +0,0 @@
# 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

Просмотреть файл

@@ -1,19 +0,0 @@
# 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

Просмотреть файл

@@ -1,16 +0,0 @@
# 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

Просмотреть файл

@@ -1,15 +0,0 @@
<!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>

Просмотреть файл

@@ -1,13 +0,0 @@
<!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 +0,0 @@
<%= yield %>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше