refactor: Remove jQuery dependency and consolidate JavaScript assets

- Rename vendor JS file to activeadmin/quill_editor.js to avoid conflicts
- Update import alias to use 'activeadmin/quill_editor' for consistency
- Remove jQuery-dependent legacy file (quill_editor_input.js)
- Simplify engine asset configuration - only one JS file now
- Update documentation with correct paths and import names
- All functionality preserved with vanilla JavaScript

BREAKING CHANGE: Drops support for jQuery-based initialization.
Applications using the legacy jQuery initialization will need to
update to the new module-based approach.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Этот коммит содержится в:
Gleb Tv
2025-09-24 14:42:54 +03:00
родитель 10ea2928a4
Коммит 0a12969370
9 изменённых файлов: 17 добавлений и 243 удалений

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

@@ -1,123 +0,0 @@
/* globals $ Quill ImageUploader */
(function () {
'use strict';
// --- functions ---------------------------------------------------------------
const initQuillEditors = () => {
const defaultTheme = 'snow';
const defaultToolbar = [
['bold', 'italic', 'underline'],
['link', 'blockquote', 'code-block'],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
[{ 'color': [] }, { 'background': [] }],
['image'],
['clean'],
];
const editors = document.querySelectorAll('[data-aa-quill-editor]');
const registeredPlugins = {};
for (let i = 0; i < editors.length; i++) {
const content = editors[i].querySelector('[data-aa-quill-content]');
const isActive = editors[i].classList.contains('quill-editor--active');
if (content && !isActive) {
// Setup editor options
const options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : {};
if (!options.theme) options.theme = defaultTheme;
if (!options.modules) options.modules = {};
if (!options.modules.toolbar) options.modules.toolbar = defaultToolbar;
// Setup plugin options
const pluginOptions = editors[i].getAttribute('data-plugins') ? JSON.parse(editors[i].getAttribute('data-plugins')) : {};
if (pluginOptions.image_uploader && pluginOptions.image_uploader.server_url) {
if (!registeredPlugins.image_uploader) {
Quill.register('modules/imageUploader', ImageUploader);
registeredPlugins.image_uploader = true;
}
const opts = pluginOptions.image_uploader;
options.modules.imageUploader = setupImageUploader(opts.server_url, opts.field_name);
}
// Init editor
editors[i]['_quill-editor'] = new Quill(content, options);
editors[i].classList += ' quill-editor--active';
}
}
const formtastic = document.querySelector('form.formtastic');
if (formtastic) {
formtastic.onsubmit = () => {
for (let i = 0; i < editors.length; i++) {
const input = editors[i].querySelector('input[type="hidden"]');
if (editors[i]['_quill-editor'].editor.isBlank()) {
input.value = '';
} else {
input.value = editors[i]['_quill-editor'].root.innerHTML;
}
}
};
}
};
const setupImageUploader = (server_url, field_name) => {
return {
upload: (file) => {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append(field_name || 'file_upload', file);
fetch(server_url, {
body: formData,
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
method: 'POST'
}).then(response => response.json())
.then(result => {
if (!result.url) {
reject('Upload failed');
}
resolve(result.url);
})
.catch(error => {
reject('Upload failed');
console.error('Error: ', error);
})
})
}
}
}
// --- public functions --------------------------------------------------------
window.getQuillEditors = function() {
const editors = document.querySelectorAll('[data-aa-quill-editor]');
const list = [];
editors.forEach(function(editor) { list.push(editor['_quill-editor']) });
return list;
}
window.getQuillEditorByIndex = function(index) {
const editors = document.querySelectorAll('[data-aa-quill-editor]');
return (index >= 0 && index < editors.length) ? editors[index]['_quill-editor'] : null;
}
window.getQuillEditorByElementId = function(id) {
const editor = document.querySelector(`[data-aa-quill-editor]#${id}`);
return editor ? editor['_quill-editor'] : null;
}
// --- events ------------------------------------------------------------------
$(document).ready(initQuillEditors);
$(document).on('has_many_add:after', '.has_many_container', initQuillEditors);
$(document).on('turbolinks:load', initQuillEditors);
})();

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

@@ -1,97 +0,0 @@
# Session Summary - ActiveAdmin Quill Editor Modernization
## Context
We analyzed replacing Trumbowyg with Quill for ActiveAdmin 4.x and Rails 8 compatibility. After evaluating SunEditor (too large at 450KB after removing source maps) and Quill.js (~200KB), we chose to modernize the existing `activeadmin_quill_editor` gem.
## Important Discoveries
1. **Existing Test App**: The gem already has a full Rails test application at `spec/dummy/` (not using Combustion)
2. **Existing Tests**: Comprehensive system tests already exist in `spec/system/`
3. **Existing Formtastic Input**: `QuillEditorInput` class already exists, just needs jQuery removal
## Key Decisions Made
1. **Use Quill.js** instead of SunEditor
- Quill: ~200KB minified
- Clean delta-based architecture
- No jQuery dependency for the core library
- Existing gem foundation to build upon
2. **Serve assets directly from vendor/assets**
- No NPM package needed initially
- Propshaft automatically serves from vendor/assets and app/assets
- Simple drop-in integration
3. **Follow activeadmin_trumbowyg patterns**
- Combustion-based test app
- Modern CI with matrix testing (Ruby 3.3/3.4, Rails 7.x/8.x)
- Comprehensive GitHub Actions setup with caching
- ESBuild for test app assets
## Files Created
- `/data/activeadmin-quill_editor/docs/activeadmin-4-propshaft-update.md` - Complete migration guide
- `/data/activeadmin-quill_editor/docs/session-summary.md` - This summary
## Next Steps
### Immediate Tasks
1. Download Quill 2.x assets and place in vendor/assets
2. Create new JavaScript initializer without jQuery
3. Update Formtastic input class (already exists, just needs updates)
4. Update existing spec/dummy test app for Propshaft/Rails 8
5. Configure GitHub Actions CI/CD
### Implementation Order
1. **Asset Setup** (vendor/assets structure)
2. **JavaScript Modernization** (remove jQuery)
3. **Test App Updates** (spec/dummy with Propshaft)
4. **CI/CD Setup** (GitHub Actions with matrix)
5. **Documentation Update** (README, examples)
### Testing Strategy
- Use existing spec/dummy Rails app (no Combustion needed)
- Update existing system tests, add missing cases from trumbowyg
- Matrix test against Ruby 3.3/3.4
- Test Rails 7.x and 8.x with ActiveAdmin 4.x beta
- Consider adding Playwright for modern browser tests
- SimpleCov for coverage with SonarQube integration
## Technical Notes
### Propshaft Asset Serving
- Gems can provide assets in `vendor/assets` and `app/assets`
- Propshaft automatically includes these paths in load path
- No compilation needed for vendored assets
- Digest stamping handled automatically
### JavaScript API Changes
Replace jQuery-based initialization:
```javascript
// Old (jQuery)
$(document).ready(initQuillEditors);
// New (Vanilla)
document.addEventListener('DOMContentLoaded', initQuillEditors);
document.addEventListener('turbo:load', initQuillEditors);
```
### Form Input Usage
```ruby
f.input :content, as: :quill_editor, input_html: {
data: {
options: {
theme: 'snow',
modules: { toolbar: [...] }
}
}
}
```
## Resources
- Quill.js 2.x: https://quilljs.com/
- Propshaft docs: https://github.com/rails/propshaft
- Reference gems:
- /data/activeadmin_trumbowyg (modern CI/test setup)
- /data/activeadmin-searchable_select (alternative patterns)
## Contact for Questions
Review the comprehensive guide at `activeadmin-4-propshaft-update.md` for complete implementation details.

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

@@ -63,8 +63,8 @@ try {
}
// 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';
// 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
@@ -130,7 +130,7 @@ const config = {
},
// 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')
'activeadmin/quill_editor': path.join(gemPath, 'vendor/assets/javascripts/activeadmin/quill_editor.js')
}
};

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

@@ -11,16 +11,10 @@ module ActiveAdmin
# No need for explicit asset path configuration with Propshaft
initializer 'activeadmin_quill_editor.assets' do |app|
# For Propshaft (Rails 8 default)
if defined?(Propshaft)
# 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
]
# For Sprockets (legacy support)
elsif app.config.respond_to?(:assets)
app.config.assets.precompile += %w[
activeadmin/quill_editor_input.js
activeadmin_quill_editor.js
activeadmin/quill_editor.js
]
end
end

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

@@ -2943,9 +2943,9 @@
}
});
// ../../vendor/assets/javascripts/activeadmin_quill_editor.js
var require_activeadmin_quill_editor = __commonJS({
"../../vendor/assets/javascripts/activeadmin_quill_editor.js"(exports2, module2) {
// ../../vendor/assets/javascripts/activeadmin/quill_editor.js
var require_quill_editor = __commonJS({
"../../vendor/assets/javascripts/activeadmin/quill_editor.js"(exports2, module2) {
(function() {
"use strict";
const defaultTheme = "snow";
@@ -17117,7 +17117,7 @@ ${escapeText(this.code(index, length))}
var quill_default = core_default;
// app/javascript/active_admin.js
var import_activeadmin_quill_editor = __toESM(require_activeadmin_quill_editor());
var import_quill_editor = __toESM(require_quill_editor());
window.Quill = quill_default;
try {
const ImageUploader = __require("quill-image-uploader");
@@ -17126,8 +17126,8 @@ ${escapeText(this.code(index, length))}
}
if (window.QuillEditor && window.QuillEditor.init) {
window.QuillEditor.init();
} else if (import_activeadmin_quill_editor.default && import_activeadmin_quill_editor.default.init) {
import_activeadmin_quill_editor.default.init();
} else if (import_quill_editor.default && import_quill_editor.default.init) {
import_quill_editor.default.init();
}
})();
/*! Bundled license information:

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

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

@@ -15,7 +15,7 @@ try {
}
// Import the Quill Editor initialization module
import QuillEditorModule from 'activeadmin_quill_editor';
import QuillEditorModule from 'activeadmin/quill_editor';
// Now that Quill is available, initialize the editors
// This ensures proper initialization order without setTimeout hacks

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

@@ -23,7 +23,7 @@ const config = {
},
// Use alias for clean imports
alias: {
'activeadmin_quill_editor': path.join(gemPath, 'vendor/assets/javascripts/activeadmin_quill_editor.js')
'activeadmin/quill_editor': path.join(gemPath, 'vendor/assets/javascripts/activeadmin/quill_editor.js')
}
};

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