├── .fasterer.yml ├── .github ├── FUNDING.yml └── workflows │ ├── linters.yml │ ├── specs_rails61.yml │ ├── specs_rails70.yml │ ├── specs_rails71.yml │ ├── specs_rails72.yml │ └── specs_rails80.yml ├── .gitignore ├── .reviewdog.yml ├── .rspec ├── .rubocop.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── Makefile ├── README.md ├── Rakefile ├── activeadmin_medium_editor.gemspec ├── app └── assets │ ├── javascripts │ └── activeadmin │ │ ├── medium_editor │ │ ├── medium_editor.js │ │ └── medium_editor.min.js │ │ └── medium_editor_input.js │ └── stylesheets │ └── activeadmin │ ├── _medium_editor_input.scss │ └── medium_editor │ ├── _settings.scss │ ├── animations │ ├── _image-loading.scss │ └── _pop-upwards.scss │ ├── components │ ├── _anchor-preview.scss │ ├── _file-dragging.scss │ ├── _placeholder.scss │ ├── _toolbar-form.scss │ └── _toolbar.scss │ ├── medium_editor.scss │ ├── themes │ ├── beagle.scss │ ├── bootstrap.scss │ ├── default.scss │ ├── flat.scss │ ├── mani.scss │ ├── roman.scss │ └── tim.scss │ └── util │ └── _clearfix.scss ├── bin ├── fasterer ├── rails ├── rake ├── rspec └── rubocop ├── extra ├── .bashrc ├── .env ├── Dockerfile ├── Dockerfile.dockerignore ├── dev_setup.sh ├── development.md ├── docker-compose.yml └── screenshot.png ├── lib ├── activeadmin │ ├── medium_editor.rb │ └── medium_editor │ │ ├── engine.rb │ │ └── version.rb ├── activeadmin_medium_editor.rb └── formtastic │ └── inputs │ └── medium_editor_input.rb └── spec ├── dummy ├── .ruby-version ├── .tool-versions ├── Rakefile ├── app │ ├── admin │ │ ├── authors.rb │ │ ├── dashboard.rb │ │ ├── posts.rb │ │ └── tags.rb │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ └── .keep │ │ ├── javascripts │ │ │ └── active_admin.js │ │ └── stylesheets │ │ │ ├── active_admin.scss │ │ │ └── application.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── helpers │ │ └── application_helper.rb │ ├── javascript │ │ └── packs │ │ │ └── application.js │ ├── jobs │ │ └── application_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ ├── author.rb │ │ ├── concerns │ │ │ └── .keep │ │ ├── post.rb │ │ ├── post_tag.rb │ │ ├── profile.rb │ │ └── tag.rb │ └── views │ │ └── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb ├── bin │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── active_admin.rb │ │ ├── active_storage.rb │ │ ├── application_controller_renderer.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── content_security_policy.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ ├── spring.rb │ └── storage.yml ├── db │ ├── migrate │ │ ├── 20170806125915_create_active_storage_tables.active_storage.rb │ │ ├── 20180101010101_create_active_admin_comments.rb │ │ ├── 20180607053251_create_authors.rb │ │ ├── 20180607053254_create_profiles.rb │ │ ├── 20180607053255_create_tags.rb │ │ ├── 20180607053257_create_post_tags.rb │ │ └── 20180607053739_create_posts.rb │ └── schema.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ └── favicon.ico └── tmp │ └── .keep ├── rails_helper.rb ├── spec_helper.rb ├── support └── capybara.rb └── system ├── medium_editor_spec.rb └── medium_js_spec.rb /.fasterer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - bin/* 4 | - db/schema.rb 5 | - gemfiles/**/* 6 | - spec/dummy/**/* 7 | - vendor/**/* 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [blocknotes] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Linters 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | reviewdog: 12 | name: Reviewdog 13 | runs-on: ubuntu-latest 14 | 15 | env: 16 | RAILS_VERSION: 7.0 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Set up Ruby 23 | uses: ruby/setup-ruby@v1 24 | with: 25 | ruby-version: 3.0 26 | bundler-cache: true 27 | 28 | - name: Set up Reviewdog 29 | uses: reviewdog/action-setup@v1 30 | with: 31 | reviewdog_version: latest 32 | 33 | - name: Run Reviewdog 34 | env: 35 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | run: | 37 | reviewdog -fail-on-error -reporter=github-pr-review -runners=fasterer,rubocop 38 | 39 | # NOTE: check with: reviewdog -fail-on-error -reporter=github-pr-review -runners=fasterer -diff="git diff" -tee 40 | -------------------------------------------------------------------------------- /.github/workflows/specs_rails61.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Specs Rails 6.1 with ActiveAdmin 2.9 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | ruby: ['3.0'] 17 | 18 | env: 19 | RAILS_VERSION: 6.0 20 | ACTIVEADMIN_VERSION: 2.9.0 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Set up Ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby }} 30 | bundler-cache: true 31 | 32 | - name: Database setup 33 | run: bin/rails db:create db:migrate db:test:prepare 34 | 35 | - name: Run tests 36 | run: bundle exec rspec --profile 37 | 38 | - name: On failure, archive screenshots as artifacts 39 | uses: actions/upload-artifact@v4 40 | if: failure() 41 | with: 42 | name: test-failed-screenshots 43 | path: spec/dummy/tmp/screenshots 44 | -------------------------------------------------------------------------------- /.github/workflows/specs_rails70.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Specs Rails 7.0 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | ruby: ['3.0', '3.2'] 17 | 18 | env: 19 | RAILS_VERSION: 7.0 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | 31 | - name: Database setup 32 | run: bin/rails db:create db:migrate db:test:prepare 33 | 34 | - name: Run tests 35 | run: bundle exec rspec --profile 36 | 37 | - name: On failure, archive screenshots as artifacts 38 | uses: actions/upload-artifact@v4 39 | if: failure() 40 | with: 41 | name: test-failed-screenshots 42 | path: spec/dummy/tmp/screenshots 43 | -------------------------------------------------------------------------------- /.github/workflows/specs_rails71.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Specs Rails 7.1 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | ruby: ['3.2', '3.4'] 17 | 18 | env: 19 | RAILS_VERSION: 7.1 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | 31 | - name: Database setup 32 | run: bin/rails db:create db:migrate db:test:prepare 33 | 34 | - name: Run tests 35 | run: bundle exec rspec --profile 36 | 37 | - name: On failure, archive screenshots as artifacts 38 | uses: actions/upload-artifact@v4 39 | if: failure() 40 | with: 41 | name: test-failed-screenshots 42 | path: spec/dummy/tmp/screenshots 43 | -------------------------------------------------------------------------------- /.github/workflows/specs_rails72.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Specs Rails 7.2 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | ruby: ['3.2', '3.4'] 17 | 18 | env: 19 | RAILS_VERSION: 7.2 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | 31 | - name: Database setup 32 | run: bin/rails db:create db:migrate db:test:prepare 33 | 34 | - name: Run tests 35 | run: bundle exec rspec --profile 36 | 37 | - name: On failure, archive screenshots as artifacts 38 | uses: actions/upload-artifact@v4 39 | if: failure() 40 | with: 41 | name: test-failed-screenshots 42 | path: spec/dummy/tmp/screenshots 43 | -------------------------------------------------------------------------------- /.github/workflows/specs_rails80.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Specs Rails 8.0 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | ruby: ['3.2', '3.4'] 17 | 18 | env: 19 | RAILS_VERSION: 8.0 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | 31 | - name: Database setup 32 | run: bin/rails db:create db:migrate db:test:prepare 33 | 34 | - name: Run tests 35 | run: bundle exec rspec --profile 36 | 37 | - name: On failure, archive screenshots as artifacts 38 | uses: actions/upload-artifact@v4 39 | if: failure() 40 | with: 41 | name: test-failed-screenshots 42 | path: spec/dummy/tmp/screenshots 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.orig 3 | 4 | /.rspec_failures 5 | /.rubocop-* 6 | /Gemfile.lock 7 | 8 | /_misc/ 9 | /spec/dummy/db/*.sqlite3* 10 | /spec/dummy/log/ 11 | /spec/dummy/storage/ 12 | /spec/dummy/tmp/ 13 | -------------------------------------------------------------------------------- /.reviewdog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | runner: 3 | fasterer: 4 | cmd: bin/fasterer 5 | level: info 6 | rubocop: 7 | cmd: bin/rubocop 8 | level: info 9 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require rails_helper 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit_from: 3 | - https://relaxed.ruby.style/rubocop.yml 4 | 5 | AllCops: 6 | Exclude: 7 | - bin/* 8 | - db/schema.rb 9 | - gemfiles/**/* 10 | - spec/dummy/**/* 11 | - vendor/**/* 12 | NewCops: enable 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # medium-editor for ActiveAdmin 2 | 3 | An Active Admin plugin to use Medium Editor. 4 | 5 | ## v1.0.0 - 2022-04-18 6 | 7 | - Set minimum Ruby version to 2.6.0 8 | - Remove `sassc` dependency 9 | - Enable Ruby 3.0 specs support 10 | - Enable Rails 7.0 specs support 11 | - Internal improvements 12 | 13 | ## v0.2.14 - 2021-03-20 14 | 15 | - Fix editor loading with Turbolinks 16 | - Specs improvements 17 | 18 | ## v0.2.12 - 2020-09-09 19 | 20 | - JS refactoring 21 | - Minor specs improvements 22 | - README changes 23 | 24 | ## v0.2.9 - 2020-09-04 25 | 26 | - Reset some styles manually to the defaults to render better the elements in the editor 27 | 28 | ## v0.2.8 - 2020-09-03 29 | 30 | - Update MediumEditor to commit `d113a744` 31 | - Add specs for editor in nested resources 32 | - Add Rubocop and remove SimpleCov gems 33 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | def eval_version(dependency, version) 6 | return [dependency] if version.empty? 7 | 8 | version.count('.') < 2 ? [dependency, "~> #{version}.0"] : [dependency, version] 9 | end 10 | 11 | if ENV['DEVEL'] == '1' 12 | gem 'activeadmin_medium_editor', path: './' 13 | else 14 | gemspec 15 | end 16 | 17 | ruby_ver = ENV.fetch('RUBY_VERSION', '') 18 | 19 | rails_ver = ENV.fetch('RAILS_VERSION', '') 20 | rails = eval_version('rails', rails_ver) 21 | gem(*rails) 22 | 23 | active_admin_ver = ENV.fetch('ACTIVEADMIN_VERSION', '') 24 | active_admin = eval_version('activeadmin', active_admin_ver) 25 | gem(*active_admin) 26 | 27 | ruby32 = ruby_ver.empty? || Gem::Version.new(ruby_ver) >= Gem::Version.new('3.2') 28 | rails72 = rails_ver.empty? || Gem::Version.new(rails_ver) >= Gem::Version.new('7.2') 29 | sqlite3 = ruby32 && rails72 ? ['sqlite3'] : ['sqlite3', '~> 1.4'] 30 | gem(*sqlite3) 31 | 32 | gem 'zeitwerk', '~> 2.6.18' unless ruby32 33 | 34 | # NOTE: to avoid error: uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger 35 | gem 'concurrent-ruby', '1.3.4' 36 | 37 | # Misc 38 | gem 'bigdecimal' 39 | gem 'csv' 40 | gem 'mutex_m' 41 | gem 'puma' 42 | gem 'sassc' 43 | gem 'sprockets-rails' 44 | 45 | # Testing 46 | gem 'capybara' 47 | gem 'cuprite' 48 | gem 'rspec_junit_formatter' 49 | gem 'rspec-rails' 50 | gem 'simplecov', require: false 51 | gem 'super_diff' 52 | 53 | # Linters 54 | gem 'fasterer' 55 | gem 'rubocop' 56 | gem 'rubocop-capybara' 57 | gem 'rubocop-packaging' 58 | gem 'rubocop-performance' 59 | gem 'rubocop-rails' 60 | gem 'rubocop-rspec' 61 | gem 'rubocop-rspec_rails' 62 | 63 | # Tools 64 | gem 'pry-rails' 65 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2020 Mattia Roccoberton 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include extra/.env 2 | 3 | help: 4 | @echo -e "${COMPOSE_PROJECT_NAME} - Main project commands:\n\ 5 | make up # starts the dev services (optional env vars: RUBY / RAILS / ACTIVEADMIN)\n\ 6 | make specs # run the tests (after up)\n\ 7 | make lint # run the linters (after up)\n\ 8 | make server # run the server (after up)\n\ 9 | make shell # open a shell (after up)\n\ 10 | make down # cleanup (after up)\n\ 11 | Example: RUBY=3.2 RAILS=7.1 ACTIVEADMIN=3.2.0 make up" 12 | 13 | # System commands 14 | 15 | build: 16 | @rm -f Gemfile.lock spec/dummy/db/*.sqlite3 17 | @docker compose -f extra/docker-compose.yml build 18 | 19 | db_reset: 20 | @docker compose -f extra/docker-compose.yml run --rm app bin/rails db:create db:migrate db:test:prepare 21 | 22 | up: build db_reset 23 | @docker compose -f extra/docker-compose.yml up 24 | 25 | shell: 26 | @docker compose -f extra/docker-compose.yml exec app bash 27 | 28 | down: 29 | @docker compose -f extra/docker-compose.yml down --volumes --rmi local --remove-orphans 30 | 31 | # App commands 32 | 33 | seed: 34 | @docker compose -f extra/docker-compose.yml exec app bin/rails db:seed 35 | 36 | console: seed 37 | @docker compose -f extra/docker-compose.yml exec app bin/rails console 38 | 39 | lint: 40 | @docker compose -f extra/docker-compose.yml exec app bin/rubocop 41 | 42 | server: seed 43 | @rm -f spec/dummy/tmp/pids/server.pid 44 | @docker compose -f extra/docker-compose.yml exec app bin/rails server -b 0.0.0.0 -p ${SERVER_PORT} 45 | 46 | specs: 47 | @docker compose -f extra/docker-compose.yml exec app bin/rspec --fail-fast 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PROJECT UNMAINTAINED 2 | 3 | > *This project is not maintained anymore* 4 | > 5 | > *If you like it or continue to use it fork it please.* 6 | 7 | --- 8 | 9 | # Active Admin Medium Editor 10 | [](https://badge.fury.io/rb/activeadmin_medium_editor) 11 | [](https://rubygems.org/gems/activeadmin_medium_editor) 12 | [](https://github.com/blocknotes/activeadmin_medium_editor/actions/workflows/linters.yml) 13 | [](https://github.com/blocknotes/activeadmin_medium_editor/actions/workflows/specs_rails61.yml) 14 | [](https://github.com/blocknotes/activeadmin_medium_editor/actions/workflows/specs_rails70.yml) 15 | 16 | An Active Admin plugin to use [medium-editor](https://github.com/yabwe/medium-editor), a compact and clean WYSIWYG editor. 17 | 18 | **IMPORTANT NOTICE**: while I like the Medium Editor idea of having floating buttons, it looks like that they are slow to release new stable versions. Some editor bugs are related to this problem unfortunately. 19 | 20 |  21 | 22 | ## Usage 23 | 24 | - After the installation, select some text in the editor 25 | - A pop-up menu is shown with the available buttons 26 | - Click on a button and the effect will be applied to the selected text 27 | 28 | ## Install 29 | 30 | - After installing Active Admin, add to your Gemfile: `gem 'activeadmin_medium_editor'` 31 | - Add also a SASS/SCSS gem to your Gemfile (ex. `gem 'sassc'`) 32 | - Add at the end of your Active Admin styles (_app/assets/stylesheets/active_admin.scss_): 33 | ```scss 34 | @import 'activeadmin/medium_editor/medium_editor'; 35 | @import 'activeadmin/medium_editor_input'; 36 | @import 'activeadmin/medium_editor/themes/default'; // or another theme 37 | ``` 38 | - Add at the end of your Active Admin javascripts (_app/assets/javascripts/active_admin.js_): 39 | ```js 40 | //= require activeadmin/medium_editor/medium_editor 41 | //= require activeadmin/medium_editor_input 42 | ``` 43 | - Use the input with `as: :medium_editor` in Active Admin model conf 44 | - **data-options**: permits to set *medium-editor* options directly - see [options list](https://github.com/yabwe/medium-editor#mediumeditor-options) (examples below) 45 | 46 | > Why 2 separated scripts/styles? In this way you can include a different version of *medium-editor* if you like 47 | 48 | > **UPDATE FROM VERSION < 0.2.8**: please change your _app/assets/stylesheets/active_admin.scss_ using the new import lines above 49 | 50 | ## Examples 51 | 52 | ### Basic usage 53 | 54 | ```ruby 55 | # Active Admin post form conf: 56 | form do |f| 57 | f.inputs 'Post' do 58 | f.input :title 59 | f.input :description, as: :medium_editor, input_html: { data: { options: '{"spellcheck":false,"toolbar":{"buttons":["bold","italic","underline","anchor"]}}' } } 60 | f.input :published 61 | end 62 | f.actions 63 | end 64 | ``` 65 | 66 | ### Buttons configuration 67 | 68 | ```ruby 69 | toolbar = { buttons: %w[bold italic underline strikethrough subscript superscript anchor image quote pre orderedlist unorderedlist indent outdent justifyLeft justifyCenter justifyRight justifyFull h1 h2 h3 h4 h5 h6 removeFormat html] } 70 | f.input :description, as: :medium_editor, input_html: { data: { options: { toolbar: toolbar } } } 71 | ``` 72 | 73 | For details about the buttons' effect please refer to medium-editor documentation. 74 | 75 | ## Changelog 76 | 77 | The changelog is available [here](CHANGELOG.md). 78 | 79 | ## Do you like it? Star it! 80 | 81 | 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). 82 | 83 | Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me). 84 | 85 | ## Contributors 86 | 87 | - [Mattia Roccoberton](https://blocknot.es): author 88 | - The good guys that opened issues and pull requests from time to time 89 | 90 | ## License 91 | 92 | The gem is available as open-source under the terms of the [MIT](LICENSE.txt). 93 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | require 'bundler/setup' 5 | rescue LoadError 6 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 7 | end 8 | 9 | APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__) 10 | load 'rails/tasks/engine.rake' 11 | 12 | load 'rails/tasks/statistics.rake' 13 | 14 | require 'bundler/gem_tasks' 15 | 16 | begin 17 | require 'rspec/core/rake_task' 18 | 19 | RSpec::Core::RakeTask.new(:spec) do |t| 20 | # t.ruby_opts = %w[-w] 21 | t.rspec_opts = ['--color', '--format documentation'] 22 | end 23 | 24 | task default: :spec 25 | rescue LoadError 26 | puts '! LoadError: no RSpec available' 27 | end 28 | -------------------------------------------------------------------------------- /activeadmin_medium_editor.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'activeadmin/medium_editor/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'activeadmin_medium_editor' 9 | spec.version = ActiveAdmin::MediumEditor::VERSION 10 | spec.summary = 'medium-editor for ActiveAdmin' 11 | spec.description = 'An Active Admin plugin to use Medium Editor' 12 | spec.license = 'MIT' 13 | spec.authors = ['Mattia Roccoberton'] 14 | spec.email = 'mat@blocknot.es' 15 | spec.homepage = 'https://github.com/blocknotes/activeadmin_medium_editor' 16 | 17 | spec.required_ruby_version = '>= 2.6.0' 18 | 19 | spec.metadata['homepage_uri'] = spec.homepage 20 | spec.metadata['changelog_uri'] = 'https://github.com/blocknotes/activeadmin_medium_editor/blob/master/CHANGELOG.md' 21 | spec.metadata['source_code_uri'] = spec.homepage 22 | 23 | spec.metadata['rubygems_mfa_required'] = 'true' 24 | 25 | spec.files = Dir['{app,lib}/**/*', 'LICENSE.txt', 'Rakefile', 'README.md'] 26 | spec.require_paths = ['lib'] 27 | 28 | spec.add_dependency 'activeadmin', '>= 2.0' 29 | end 30 | -------------------------------------------------------------------------------- /app/assets/javascripts/activeadmin/medium_editor_input.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict' 3 | 4 | // --- functions ------------------------------------------------------------- 5 | function initMediumEditors() { 6 | $('[data-aa-medium-editor]').each(function () { 7 | if (!$(this).hasClass('medium-editor--active')) { 8 | let options = {} 9 | options = $.extend({}, options, $(this).data('options')) 10 | new MediumEditor($(this), options) 11 | $(this).addClass('medium-editor--active') 12 | } 13 | }) 14 | } 15 | 16 | // --- events ---------------------------------------------------------------- 17 | $(document).ready(initMediumEditors) 18 | $(document).on('has_many_add:after', '.has_many_container', initMediumEditors) 19 | $(document).on('turbolinks:load', initMediumEditors) 20 | })() 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/_medium_editor_input.scss: -------------------------------------------------------------------------------- 1 | body.active_admin form { 2 | textarea.medium-editor-hidden{ 3 | display: none; 4 | } 5 | 6 | .medium-editor-element { 7 | background-color: #fff; 8 | border-radius: 3px; 9 | border: 1px solid #c9d0d6; 10 | display: inline-block; 11 | max-height: 200px; 12 | min-height: 100px; 13 | overflow-y: scroll; 14 | padding: 8px 10px 7px; 15 | width: calc(80% - 22px); 16 | 17 | // reset internal elements 18 | * { 19 | margin: initial; 20 | padding: initial; 21 | text-align: initial; 22 | } 23 | 24 | h1 { 25 | margin-top: 0.67em; 26 | margin-bottom: 0.67em; 27 | } 28 | 29 | h2 { 30 | margin-top: 0.83em; 31 | margin-bottom: 0.83em; 32 | } 33 | 34 | h3 { 35 | margin-top: 1em; 36 | margin-bottom: 1em; 37 | } 38 | 39 | h4 { 40 | margin-top: 1.33em; 41 | margin-bottom: 1.33em; 42 | } 43 | 44 | h5 { 45 | margin-top: 1.67em; 46 | margin-bottom: 1.67em; 47 | } 48 | 49 | h6 { 50 | margin-top: 2.33em; 51 | margin-bottom: 2.33em; 52 | } 53 | 54 | blockquote { 55 | margin: 1.5em; 56 | } 57 | 58 | ol { 59 | list-style-type: decimal; 60 | } 61 | 62 | p { 63 | margin-top: 1em; 64 | margin-bottom: 1em; 65 | } 66 | 67 | ul { 68 | list-style-type: disc; 69 | } 70 | 71 | ul, ol { 72 | margin: 1em 2em 1em 0; 73 | padding-left: 2em; 74 | } 75 | } 76 | 77 | .medium-editor-element:focus { 78 | border-color: transparent; 79 | } 80 | 81 | .medium_editor > label { 82 | display: inline-block; 83 | float: none; 84 | vertical-align: top; 85 | } 86 | } 87 | 88 | button.medium-editor-action { 89 | border-radius: 0; 90 | } 91 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/_settings.scss: -------------------------------------------------------------------------------- 1 | // typography 2 | $font-fixed: Consolas, "Liberation Mono", Menlo, Courier, monospace !default; 3 | $font-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; 4 | 5 | // ui / positioning 6 | $z-toolbar: 2000 !default; 7 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/animations/_image-loading.scss: -------------------------------------------------------------------------------- 1 | @keyframes medium-editor-image-loading { 2 | 0% { 3 | transform: scale(0) 4 | } 5 | 100% { 6 | transform: scale(1); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/animations/_pop-upwards.scss: -------------------------------------------------------------------------------- 1 | @keyframes medium-editor-pop-upwards { 2 | 0% { 3 | opacity: 0; 4 | transform: matrix(.97, 0, 0, 1, 0, 12); 5 | } 6 | 7 | 20% { 8 | opacity: .7; 9 | transform: matrix(.99, 0, 0, 1, 0, 2); 10 | } 11 | 12 | 40% { 13 | opacity: 1; 14 | transform: matrix(1, 0, 0, 1, 0, -1); 15 | } 16 | 17 | 100% { 18 | transform: matrix(1, 0, 0, 1, 0, 0); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/components/_anchor-preview.scss: -------------------------------------------------------------------------------- 1 | .medium-editor-anchor-preview { 2 | font-family: $font-sans-serif; 3 | font-size: 16px; 4 | left: 0; 5 | line-height: 1.4; 6 | max-width: 280px; 7 | position: absolute; 8 | text-align: center; 9 | top: 0; 10 | word-break: break-all; 11 | word-wrap: break-word; 12 | visibility: hidden; 13 | z-index: $z-toolbar; 14 | 15 | a { 16 | color: #fff; 17 | display: inline-block; 18 | margin: 5px 5px 10px; 19 | } 20 | } 21 | 22 | .medium-editor-anchor-preview-active { 23 | visibility: visible; 24 | } 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/components/_file-dragging.scss: -------------------------------------------------------------------------------- 1 | .medium-editor-dragover { 2 | background: #ddd; 3 | } 4 | 5 | .medium-editor-image-loading { 6 | animation: medium-editor-image-loading 1s infinite ease-in-out; 7 | background-color: #333; 8 | border-radius: 100%; 9 | display: inline-block; 10 | height: 40px; 11 | width: 40px; 12 | } 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/components/_placeholder.scss: -------------------------------------------------------------------------------- 1 | .medium-editor-placeholder { 2 | position: relative; 3 | 4 | &:after { 5 | content: attr(data-placeholder) !important; 6 | font-style: italic; 7 | position: absolute; 8 | left: 0; 9 | top: 0; 10 | white-space: pre; 11 | padding: inherit; 12 | margin: inherit; 13 | } 14 | } 15 | 16 | .medium-editor-placeholder-relative { 17 | position: relative; 18 | 19 | &:after { 20 | content: attr(data-placeholder) !important; 21 | font-style: italic; 22 | position: relative; 23 | white-space: pre; 24 | padding: inherit; 25 | margin: inherit; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/components/_toolbar-form.scss: -------------------------------------------------------------------------------- 1 | .medium-editor-toolbar-form { 2 | display: none; 3 | 4 | input, 5 | a { 6 | font-family: $font-sans-serif; 7 | } 8 | 9 | .medium-editor-toolbar-form-row { 10 | line-height: 14px; 11 | margin-left: 5px; 12 | padding-bottom: 5px; 13 | } 14 | 15 | .medium-editor-toolbar-input, 16 | label { 17 | border: none; 18 | box-sizing: border-box; 19 | font-size: 14px; 20 | margin: 0; 21 | padding: 6px; 22 | width: 316px; 23 | display: inline-block; 24 | 25 | &:focus { 26 | appearance: none; 27 | border: none; 28 | box-shadow: none; 29 | outline: 0; 30 | } 31 | } 32 | 33 | a { 34 | display: inline-block; 35 | font-size: 24px; 36 | font-weight: bolder; 37 | margin: 0 10px; 38 | text-decoration: none; 39 | } 40 | } 41 | 42 | .medium-editor-toolbar-form-active { 43 | display: block; 44 | } 45 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/components/_toolbar.scss: -------------------------------------------------------------------------------- 1 | %medium-toolbar-arrow { 2 | border-style: solid; 3 | content: ''; 4 | display: block; 5 | height: 0; 6 | left: 50%; 7 | margin-left: -8px; 8 | position: absolute; 9 | width: 0; 10 | } 11 | 12 | .medium-toolbar-arrow-under:after { 13 | @extend %medium-toolbar-arrow; 14 | border-width: 8px 8px 0 8px; 15 | } 16 | 17 | .medium-toolbar-arrow-over:before { 18 | @extend %medium-toolbar-arrow; 19 | border-width: 0 8px 8px 8px; 20 | top: -8px; 21 | } 22 | 23 | .medium-editor-toolbar { 24 | font-family: $font-sans-serif; 25 | font-size: 16px; 26 | left: 0; 27 | position: absolute; 28 | top: 0; 29 | visibility: hidden; 30 | z-index: $z-toolbar; 31 | 32 | ul { 33 | margin: 0; 34 | padding: 0; 35 | } 36 | 37 | li { 38 | float: left; 39 | list-style: none; 40 | margin: 0; 41 | padding: 0; 42 | 43 | button { 44 | box-sizing: border-box; 45 | cursor: pointer; 46 | display: block; 47 | font-size: 14px; 48 | line-height: 1.33; 49 | margin: 0; 50 | padding: 15px; 51 | text-decoration: none; 52 | 53 | &:focus { 54 | outline: none; 55 | } 56 | } 57 | 58 | .medium-editor-action-underline { 59 | text-decoration: underline; 60 | } 61 | 62 | .medium-editor-action-pre { 63 | font-family: $font-fixed; 64 | font-size: 12px; 65 | font-weight: 100; 66 | padding: 15px 0; 67 | } 68 | } 69 | } 70 | 71 | .medium-editor-toolbar-active { 72 | visibility: visible; 73 | } 74 | 75 | .medium-editor-sticky-toolbar { 76 | position: fixed; 77 | top: 1px; 78 | } 79 | 80 | .medium-editor-relative-toolbar { 81 | position: relative; 82 | } 83 | 84 | .medium-editor-toolbar-active.medium-editor-stalker-toolbar { 85 | animation: medium-editor-pop-upwards 160ms forwards linear; 86 | } 87 | 88 | .medium-editor-toolbar-actions { 89 | @extend %clearfix; 90 | } 91 | 92 | .medium-editor-action-bold { 93 | font-weight: bolder; 94 | } 95 | 96 | .medium-editor-action-italic { 97 | font-style: italic; 98 | } 99 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/medium_editor.scss: -------------------------------------------------------------------------------- 1 | @import "settings"; 2 | @import "animations/image-loading"; 3 | @import "animations/pop-upwards"; 4 | @import "components/anchor-preview"; 5 | @import "components/file-dragging"; 6 | @import "components/placeholder"; 7 | @import "components/toolbar"; 8 | @import "components/toolbar-form"; 9 | @import "util/clearfix"; 10 | 11 | // contenteditable rules 12 | .medium-editor-element { 13 | word-wrap: break-word; 14 | min-height: 30px; 15 | 16 | img { 17 | max-width: 100%; 18 | } 19 | 20 | sub { 21 | vertical-align: sub; 22 | } 23 | 24 | sup { 25 | vertical-align: super; 26 | } 27 | } 28 | 29 | .medium-editor-hidden { 30 | display: none; 31 | } 32 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/beagle.scss: -------------------------------------------------------------------------------- 1 | // theme settings 2 | $medium-editor-bgcolor: #000; 3 | $medium-editor-button-size: 40px; 4 | $medium-editor-button-active-text-color: #a2d7c7; 5 | $medium-editor-hover-color: $medium-editor-bgcolor; 6 | $medium-editor-link-color: #ccc; 7 | $medium-editor-border-radius: 50px; 8 | $medium-editor-placeholder-color: #f8f5f3; 9 | 10 | // theme rules 11 | .medium-toolbar-arrow-under:after { 12 | border-color: $medium-editor-bgcolor transparent transparent transparent; 13 | top: $medium-editor-button-size; 14 | } 15 | 16 | .medium-toolbar-arrow-over:before { 17 | border-color: transparent transparent $medium-editor-bgcolor transparent; 18 | } 19 | 20 | .medium-editor-toolbar { 21 | background-color: $medium-editor-bgcolor; 22 | border: none; 23 | border-radius: $medium-editor-border-radius; 24 | 25 | li { 26 | button { 27 | background-color: transparent; 28 | border: none; 29 | box-sizing: border-box; 30 | color: $medium-editor-link-color; 31 | height: $medium-editor-button-size; 32 | min-width: $medium-editor-button-size; 33 | padding: 5px 12px; 34 | transition: background-color .2s ease-in, color .2s ease-in; 35 | &:hover { 36 | background-color: $medium-editor-hover-color; 37 | color: $medium-editor-button-active-text-color; 38 | } 39 | } 40 | 41 | .medium-editor-button-first { 42 | border-bottom-left-radius: $medium-editor-border-radius; 43 | border-top-left-radius: $medium-editor-border-radius; 44 | padding-left: 24px; 45 | } 46 | 47 | .medium-editor-button-last { 48 | border-bottom-right-radius: $medium-editor-border-radius; 49 | border-right: none; 50 | border-top-right-radius: $medium-editor-border-radius; 51 | padding-right: 24px 52 | } 53 | 54 | .medium-editor-button-active { 55 | background-color: $medium-editor-hover-color; 56 | color: $medium-editor-button-active-text-color; 57 | } 58 | } 59 | } 60 | 61 | .medium-editor-toolbar-form { 62 | background: $medium-editor-bgcolor; 63 | border-radius: $medium-editor-border-radius; 64 | color: $medium-editor-link-color; 65 | overflow: hidden; 66 | 67 | .medium-editor-toolbar-input { 68 | background: $medium-editor-bgcolor; 69 | box-sizing: border-box; 70 | color: $medium-editor-link-color; 71 | height: $medium-editor-button-size; 72 | padding-left: 16px; 73 | width: 220px; 74 | 75 | &::-webkit-input-placeholder { 76 | color: $medium-editor-placeholder-color; 77 | color: rgba($medium-editor-placeholder-color, .8); 78 | } 79 | &:-moz-placeholder { /* Firefox 18- */ 80 | color: $medium-editor-placeholder-color; 81 | color: rgba($medium-editor-placeholder-color, .8); 82 | } 83 | &::-moz-placeholder { /* Firefox 19+ */ 84 | color: $medium-editor-placeholder-color; 85 | color: rgba($medium-editor-placeholder-color, .8); 86 | } 87 | &:-ms-input-placeholder { 88 | color: $medium-editor-placeholder-color; 89 | color: rgba($medium-editor-placeholder-color, .8); 90 | } 91 | } 92 | 93 | a { 94 | color: $medium-editor-link-color; 95 | transform: translateY(2px); 96 | } 97 | 98 | .medium-editor-toolbar-close { 99 | margin-right: 16px; 100 | } 101 | } 102 | 103 | .medium-editor-toolbar-anchor-preview { 104 | background: $medium-editor-bgcolor; 105 | border-radius: $medium-editor-border-radius; 106 | padding: 5px 12px; 107 | } 108 | 109 | .medium-editor-anchor-preview { 110 | a { 111 | color: $medium-editor-link-color; 112 | text-decoration: none; 113 | } 114 | } 115 | 116 | .medium-editor-toolbar-actions { 117 | li, button { 118 | border-radius: $medium-editor-border-radius; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/bootstrap.scss: -------------------------------------------------------------------------------- 1 | // theme settings 2 | $medium-editor-bgcolor: #428bca; 3 | $medium-editor-border-color: #357ebd; 4 | $medium-editor-button-size: 60px; 5 | $medium-editor-button-active-text-color: #fff; 6 | $medium-editor-hover-color: #3276b1; 7 | $medium-editor-link-color: #fff; 8 | $medium-editor-border-radius: 4px; 9 | $medium-editor-placeholder-color: #fff; 10 | 11 | // theme rules 12 | .medium-toolbar-arrow-under:after { 13 | border-color: $medium-editor-bgcolor transparent transparent transparent; 14 | top: $medium-editor-button-size; 15 | } 16 | 17 | .medium-toolbar-arrow-over:before { 18 | border-color: transparent transparent $medium-editor-bgcolor transparent; 19 | } 20 | 21 | .medium-editor-toolbar { 22 | background-color: $medium-editor-bgcolor; 23 | border: 1px solid $medium-editor-border-color; 24 | border-radius: $medium-editor-border-radius; 25 | 26 | li { 27 | button { 28 | background-color: transparent; 29 | border: none; 30 | border-right: 1px solid $medium-editor-border-color; 31 | box-sizing: border-box; 32 | color: $medium-editor-link-color; 33 | height: $medium-editor-button-size; 34 | min-width: $medium-editor-button-size; 35 | transition: background-color .2s ease-in, color .2s ease-in; 36 | &:hover { 37 | background-color: $medium-editor-hover-color; 38 | color: $medium-editor-button-active-text-color; 39 | } 40 | } 41 | 42 | .medium-editor-button-first { 43 | border-bottom-left-radius: $medium-editor-border-radius; 44 | border-top-left-radius: $medium-editor-border-radius; 45 | } 46 | 47 | .medium-editor-button-last { 48 | border-bottom-right-radius: $medium-editor-border-radius; 49 | border-right: none; 50 | border-top-right-radius: $medium-editor-border-radius; 51 | } 52 | 53 | .medium-editor-button-active { 54 | background-color: $medium-editor-hover-color; 55 | color: $medium-editor-button-active-text-color; 56 | } 57 | } 58 | } 59 | 60 | .medium-editor-toolbar-form { 61 | background: $medium-editor-bgcolor; 62 | border-radius: $medium-editor-border-radius; 63 | color: #fff; 64 | 65 | .medium-editor-toolbar-input { 66 | background: $medium-editor-bgcolor; 67 | color: $medium-editor-link-color; 68 | height: $medium-editor-button-size; 69 | 70 | &::-webkit-input-placeholder { 71 | color: $medium-editor-placeholder-color; 72 | color: rgba($medium-editor-placeholder-color, .8); 73 | } 74 | &:-moz-placeholder { /* Firefox 18- */ 75 | color: $medium-editor-placeholder-color; 76 | color: rgba($medium-editor-placeholder-color, .8); 77 | } 78 | &::-moz-placeholder { /* Firefox 19+ */ 79 | color: $medium-editor-placeholder-color; 80 | color: rgba($medium-editor-placeholder-color, .8); 81 | } 82 | &:-ms-input-placeholder { 83 | color: $medium-editor-placeholder-color; 84 | color: rgba($medium-editor-placeholder-color, .8); 85 | } 86 | } 87 | 88 | a { 89 | color: $medium-editor-link-color; 90 | } 91 | } 92 | 93 | .medium-editor-toolbar-anchor-preview { 94 | background: $medium-editor-bgcolor; 95 | border-radius: $medium-editor-border-radius; 96 | color: $medium-editor-link-color; 97 | } 98 | 99 | .medium-editor-placeholder:after { 100 | color: $medium-editor-border-color; 101 | } 102 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/default.scss: -------------------------------------------------------------------------------- 1 | // theme settings 2 | $medium-editor-bgcolor: #242424; 3 | $medium-editor-button-size: 50px; 4 | $medium-editor-border-radius: 5px; 5 | 6 | // theme rules 7 | .medium-toolbar-arrow-under:after { 8 | border-color: $medium-editor-bgcolor transparent transparent transparent; 9 | top: $medium-editor-button-size; 10 | } 11 | 12 | .medium-toolbar-arrow-over:before { 13 | border-color: transparent transparent $medium-editor-bgcolor transparent; 14 | top: -8px; 15 | } 16 | 17 | .medium-editor-toolbar { 18 | background-color: $medium-editor-bgcolor; 19 | background: linear-gradient(to bottom, $medium-editor-bgcolor, rgba($medium-editor-bgcolor, 0.75)); 20 | border: 1px solid #000; 21 | border-radius: $medium-editor-border-radius; 22 | box-shadow: 0 0 3px #000; 23 | 24 | li { 25 | button { 26 | background-color: $medium-editor-bgcolor; 27 | background: linear-gradient(to bottom, $medium-editor-bgcolor, rgba($medium-editor-bgcolor, 0.89)); 28 | border: 0; 29 | border-right: 1px solid #000; 30 | border-left: 1px solid #333; 31 | border-left: 1px solid rgba(#fff, .1); 32 | box-shadow: 0 2px 2px rgba(0,0,0,0.3); 33 | color: #fff; 34 | height: $medium-editor-button-size; 35 | min-width: $medium-editor-button-size; 36 | transition: background-color .2s ease-in; 37 | 38 | &:hover { 39 | background-color: #000; 40 | color: yellow; 41 | } 42 | } 43 | 44 | .medium-editor-button-first { 45 | border-bottom-left-radius: $medium-editor-border-radius; 46 | border-top-left-radius: $medium-editor-border-radius; 47 | } 48 | 49 | .medium-editor-button-last { 50 | border-bottom-right-radius: $medium-editor-border-radius; 51 | border-top-right-radius: $medium-editor-border-radius; 52 | } 53 | 54 | .medium-editor-button-active { 55 | background-color: #000; 56 | background: linear-gradient(to bottom, $medium-editor-bgcolor, rgba(#000, 0.89)); 57 | color: #fff; 58 | } 59 | } 60 | } 61 | 62 | .medium-editor-toolbar-form { 63 | background: $medium-editor-bgcolor; 64 | border-radius: $medium-editor-border-radius; 65 | color: #999; 66 | 67 | .medium-editor-toolbar-input { 68 | background: $medium-editor-bgcolor; 69 | box-sizing: border-box; 70 | color: #ccc; 71 | height: $medium-editor-button-size; 72 | } 73 | 74 | a { 75 | color: #fff; 76 | } 77 | } 78 | 79 | .medium-editor-toolbar-anchor-preview { 80 | background: $medium-editor-bgcolor; 81 | border-radius: $medium-editor-border-radius; 82 | color: #fff; 83 | } 84 | 85 | .medium-editor-placeholder:after { 86 | color: #b3b3b1; 87 | } 88 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/flat.scss: -------------------------------------------------------------------------------- 1 | // theme settings 2 | $medium-editor-bgcolor: #57ad68; 3 | $medium-editor-border-color: #fff; 4 | $medium-editor-button-size: 60px; 5 | $medium-editor-button-active-text-color: #fff; 6 | $medium-editor-link-color: #fff; 7 | $medium-editor-placeholder-color: #fff; 8 | 9 | // theme rules 10 | .medium-toolbar-arrow-under:after { 11 | top: $medium-editor-button-size; 12 | border-color: $medium-editor-bgcolor transparent transparent transparent; 13 | } 14 | 15 | .medium-toolbar-arrow-over:before { 16 | top: -8px; 17 | border-color: transparent transparent $medium-editor-bgcolor transparent; 18 | } 19 | 20 | .medium-editor-toolbar { 21 | background-color: $medium-editor-bgcolor; 22 | 23 | li { 24 | padding: 0; 25 | 26 | button { 27 | min-width: $medium-editor-button-size; 28 | height: $medium-editor-button-size; 29 | border: none; 30 | border-right: 1px solid lighten($medium-editor-bgcolor, 20); 31 | background-color: transparent; 32 | color: $medium-editor-link-color; 33 | transition: background-color .2s ease-in, color .2s ease-in; 34 | &:hover { 35 | background-color: darken($medium-editor-bgcolor, 20); 36 | color: $medium-editor-button-active-text-color; 37 | } 38 | } 39 | 40 | .medium-editor-button-active { 41 | background-color: darken($medium-editor-bgcolor, 30); 42 | color: $medium-editor-button-active-text-color; 43 | } 44 | 45 | .medium-editor-button-last { 46 | border-right: none; 47 | } 48 | } 49 | } 50 | 51 | .medium-editor-toolbar-form { 52 | .medium-editor-toolbar-input { 53 | height: $medium-editor-button-size; 54 | background: $medium-editor-bgcolor; 55 | color: $medium-editor-link-color; 56 | 57 | &::-webkit-input-placeholder { 58 | color: $medium-editor-placeholder-color; 59 | color: rgba($medium-editor-placeholder-color, .8); 60 | } 61 | 62 | &:-moz-placeholder { /* Firefox 18- */ 63 | color: $medium-editor-placeholder-color; 64 | color: rgba($medium-editor-placeholder-color, .8); 65 | } 66 | 67 | &::-moz-placeholder { /* Firefox 19+ */ 68 | color: $medium-editor-placeholder-color; 69 | color: rgba($medium-editor-placeholder-color, .8); 70 | } 71 | 72 | &:-ms-input-placeholder { 73 | color: $medium-editor-placeholder-color; 74 | color: rgba($medium-editor-placeholder-color, .8); 75 | } 76 | } 77 | 78 | a { 79 | color: $medium-editor-link-color; 80 | } 81 | } 82 | 83 | .medium-editor-toolbar-anchor-preview { 84 | background: $medium-editor-bgcolor; 85 | color: $medium-editor-link-color; 86 | } 87 | 88 | .medium-editor-placeholder:after { 89 | color: lighten($medium-editor-bgcolor, 20); 90 | } 91 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/mani.scss: -------------------------------------------------------------------------------- 1 | // inspired by http://dribbble.com/shots/857472-Toolbar 2 | 3 | // theme settings 4 | $medium-editor-bgcolor: #dee7f0; 5 | $medium-editor-bgcolor-alt: #5c90c7; 6 | $medium-editor-border-color: #cdd6e0; 7 | $medium-editor-button-size: 50px; 8 | $medium-editor-button-hover-text-color: #fff; 9 | $medium-editor-button-active-text-color: #000; 10 | $medium-editor-link-color: #40648a; 11 | $medium-editor-border-radius: 2px; 12 | 13 | // theme rules 14 | .medium-toolbar-arrow-under:after, 15 | .medium-toolbar-arrow-over:before { 16 | display: none; 17 | } 18 | 19 | .medium-editor-toolbar { 20 | border: 1px solid $medium-editor-border-color; 21 | background-color: $medium-editor-bgcolor; 22 | background-color: rgba($medium-editor-bgcolor, .95); 23 | background: linear-gradient(to top, $medium-editor-bgcolor, rgba(#fff, 1)); 24 | border-radius: $medium-editor-border-radius; 25 | box-shadow: 0 2px 6px rgba(#000, .45); 26 | 27 | li { 28 | button { 29 | min-width: $medium-editor-button-size; 30 | height: $medium-editor-button-size; 31 | border: none; 32 | border-right: 1px solid $medium-editor-border-color; 33 | background-color: transparent; 34 | color: $medium-editor-link-color; 35 | transition: background-color .2s ease-in, color .2s ease-in; 36 | &:hover { 37 | background-color: $medium-editor-bgcolor-alt; 38 | background-color: rgba($medium-editor-bgcolor-alt, .45); 39 | color: $medium-editor-button-hover-text-color; 40 | } 41 | } 42 | 43 | .medium-editor-button-first { 44 | border-top-left-radius: $medium-editor-border-radius; 45 | border-bottom-left-radius: $medium-editor-border-radius; 46 | } 47 | 48 | .medium-editor-button-last { 49 | border-top-right-radius: $medium-editor-border-radius; 50 | border-bottom-right-radius: $medium-editor-border-radius; 51 | } 52 | 53 | .medium-editor-button-active { 54 | background-color: $medium-editor-bgcolor-alt; 55 | background-color: rgba($medium-editor-bgcolor-alt, .45); 56 | color: $medium-editor-button-active-text-color; 57 | background: linear-gradient(to bottom, $medium-editor-bgcolor, rgba(#000, .1)); 58 | } 59 | } 60 | } 61 | 62 | .medium-editor-toolbar-form { 63 | background: $medium-editor-bgcolor; 64 | color: #999; 65 | border-radius: $medium-editor-border-radius; 66 | 67 | .medium-editor-toolbar-input { 68 | height: $medium-editor-button-size; 69 | background: $medium-editor-bgcolor; 70 | color: $medium-editor-link-color; 71 | box-sizing: border-box; 72 | } 73 | 74 | a { 75 | color: $medium-editor-link-color; 76 | } 77 | } 78 | 79 | .medium-editor-toolbar-anchor-preview { 80 | background: $medium-editor-bgcolor; 81 | color: $medium-editor-link-color; 82 | border-radius: $medium-editor-border-radius; 83 | } 84 | 85 | .medium-editor-placeholder:after { 86 | color: $medium-editor-border-color; 87 | } 88 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/roman.scss: -------------------------------------------------------------------------------- 1 | // inspired by http://dribbble.com/shots/848100-Toolbar-Psd 2 | 3 | // theme settings 4 | $medium-editor-bgcolor: #fff; 5 | $medium-editor-border-color: #a8a8a8; 6 | $medium-editor-button-size: 50px; 7 | $medium-editor-button-hover-text-color: #fff; 8 | $medium-editor-button-active-text-color: #000; 9 | $medium-editor-link-color: #889aac; 10 | $medium-editor-border-radius: 5px; 11 | 12 | // theme rules 13 | .medium-toolbar-arrow-under:after, 14 | .medium-toolbar-arrow-over:before { 15 | display: none; 16 | } 17 | 18 | .medium-editor-toolbar { 19 | background-color: $medium-editor-bgcolor; 20 | background-color: rgba($medium-editor-bgcolor, .95); 21 | border-radius: $medium-editor-border-radius; 22 | box-shadow: 0 2px 6px rgba(#000, .45); 23 | 24 | li { 25 | button { 26 | min-width: $medium-editor-button-size; 27 | height: $medium-editor-button-size; 28 | border: none; 29 | border-right: 1px solid $medium-editor-border-color; 30 | background-color: transparent; 31 | color: $medium-editor-link-color; 32 | box-shadow: inset 0 0 3px #f8f8e6; 33 | background: linear-gradient(to bottom, $medium-editor-bgcolor, rgba(#000, .2)); 34 | text-shadow: 1px 4px 6px #def, 0 0 0 #000, 1px 4px 6px #def; 35 | transition: background-color .2s ease-in; 36 | &:hover { 37 | background-color: #fff; 38 | color: $medium-editor-button-hover-text-color; 39 | color: rgba(#000, .8); 40 | } 41 | } 42 | 43 | .medium-editor-button-first { 44 | border-top-left-radius: $medium-editor-border-radius; 45 | border-bottom-left-radius: $medium-editor-border-radius; 46 | } 47 | 48 | .medium-editor-button-last { 49 | border-top-right-radius: $medium-editor-border-radius; 50 | border-bottom-right-radius: $medium-editor-border-radius; 51 | } 52 | 53 | .medium-editor-button-active { 54 | background-color: #ccc; 55 | color: $medium-editor-button-active-text-color; 56 | color: rgba(#000, .8); 57 | background: linear-gradient(to top, $medium-editor-bgcolor, rgba(#000, .1)); 58 | } 59 | } 60 | } 61 | 62 | .medium-editor-toolbar-form { 63 | background: $medium-editor-bgcolor; 64 | color: #999; 65 | border-radius: $medium-editor-border-radius; 66 | 67 | .medium-editor-toolbar-input { 68 | margin: 0; 69 | height: $medium-editor-button-size; 70 | background: $medium-editor-bgcolor; 71 | color: $medium-editor-border-color; 72 | } 73 | 74 | a { 75 | color: $medium-editor-link-color; 76 | } 77 | } 78 | 79 | .medium-editor-toolbar-anchor-preview { 80 | background: $medium-editor-bgcolor; 81 | color: $medium-editor-link-color; 82 | border-radius: $medium-editor-border-radius; 83 | } 84 | 85 | .medium-editor-placeholder:after { 86 | color: $medium-editor-border-color; 87 | } 88 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/themes/tim.scss: -------------------------------------------------------------------------------- 1 | // theme settings 2 | $medium-editor-bgcolor: #2f1e07; 3 | $medium-editor-border-color: lighten($medium-editor-bgcolor, 10); 4 | $medium-editor-button-size: 60px; 5 | $medium-editor-button-active-text-color: #ffedd5; 6 | $medium-editor-hover-color: darken($medium-editor-bgcolor, 10); 7 | $medium-editor-link-color: #ffedd5; 8 | $medium-editor-border-radius: 6px; 9 | $medium-editor-placeholder-color: #ffedd5; 10 | 11 | // theme rules 12 | .medium-toolbar-arrow-under:after { 13 | border-color: $medium-editor-bgcolor transparent transparent transparent; 14 | top: $medium-editor-button-size; 15 | } 16 | 17 | .medium-toolbar-arrow-over:before { 18 | border-color: transparent transparent $medium-editor-bgcolor transparent; 19 | } 20 | 21 | .medium-editor-toolbar { 22 | background-color: $medium-editor-bgcolor; 23 | border: 1px solid $medium-editor-border-color; 24 | border-radius: $medium-editor-border-radius; 25 | 26 | li { 27 | button { 28 | background-color: transparent; 29 | border: none; 30 | border-right: 1px solid $medium-editor-border-color; 31 | box-sizing: border-box; 32 | color: $medium-editor-link-color; 33 | height: $medium-editor-button-size; 34 | min-width: $medium-editor-button-size; 35 | transition: background-color .2s ease-in, color .2s ease-in; 36 | &:hover { 37 | background-color: $medium-editor-hover-color; 38 | color: $medium-editor-button-active-text-color; 39 | } 40 | } 41 | 42 | .medium-editor-button-first { 43 | border-bottom-left-radius: $medium-editor-border-radius; 44 | border-top-left-radius: $medium-editor-border-radius; 45 | } 46 | 47 | .medium-editor-button-last { 48 | border-bottom-right-radius: $medium-editor-border-radius; 49 | border-right: none; 50 | border-top-right-radius: $medium-editor-border-radius; 51 | } 52 | 53 | .medium-editor-button-active { 54 | background-color: $medium-editor-hover-color; 55 | color: $medium-editor-button-active-text-color; 56 | } 57 | } 58 | } 59 | 60 | .medium-editor-toolbar-form { 61 | background: $medium-editor-bgcolor; 62 | border-radius: $medium-editor-border-radius; 63 | color: #ffedd5; 64 | 65 | .medium-editor-toolbar-input { 66 | background: $medium-editor-bgcolor; 67 | color: $medium-editor-link-color; 68 | height: $medium-editor-button-size; 69 | 70 | &::-webkit-input-placeholder { 71 | color: $medium-editor-placeholder-color; 72 | color: rgba($medium-editor-placeholder-color, .8); 73 | } 74 | &:-moz-placeholder { /* Firefox 18- */ 75 | color: $medium-editor-placeholder-color; 76 | color: rgba($medium-editor-placeholder-color, .8); 77 | } 78 | &::-moz-placeholder { /* Firefox 19+ */ 79 | color: $medium-editor-placeholder-color; 80 | color: rgba($medium-editor-placeholder-color, .8); 81 | } 82 | &:-ms-input-placeholder { 83 | color: $medium-editor-placeholder-color; 84 | color: rgba($medium-editor-placeholder-color, .8); 85 | } 86 | } 87 | 88 | a { 89 | color: $medium-editor-link-color; 90 | } 91 | } 92 | 93 | .medium-editor-toolbar-anchor-preview { 94 | background: $medium-editor-bgcolor; 95 | border-radius: $medium-editor-border-radius; 96 | color: $medium-editor-link-color; 97 | } 98 | 99 | .medium-editor-placeholder:after { 100 | color: $medium-editor-border-color; 101 | } 102 | -------------------------------------------------------------------------------- /app/assets/stylesheets/activeadmin/medium_editor/util/_clearfix.scss: -------------------------------------------------------------------------------- 1 | %clearfix { 2 | &:after { 3 | clear: both; 4 | content: ""; 5 | display: table; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /bin/fasterer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'fasterer' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("fasterer", "fasterer") 30 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENV['RAILS_ENV'] ||= 'test' 6 | 7 | ENGINE_ROOT = File.expand_path('..', __dir__) 8 | ENGINE_PATH = File.expand_path('../lib/activeadmin_medium_editor', __dir__) 9 | APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__) 10 | 11 | # Set up gems listed in the Gemfile. 12 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 13 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 14 | 15 | require 'rails/all' 16 | require 'rails/engine/commands' 17 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rake' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("rake", "rake") 30 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rspec' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("rspec-core", "rspec") 30 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rubocop' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("rubocop", "rubocop") 30 | -------------------------------------------------------------------------------- /extra/.bashrc: -------------------------------------------------------------------------------- 1 | alias ls='ls --color' 2 | alias ll='ls -l' 3 | alias la='ls -la' 4 | -------------------------------------------------------------------------------- /extra/.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=activeadmin_medium_editor 2 | 3 | BUNDLER_VERSION=2.5.23 4 | SERVER_PORT=4000 5 | 6 | UID=1000 7 | GID=1000 8 | -------------------------------------------------------------------------------- /extra/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG RUBY_IMAGE=ruby:3 2 | FROM ${RUBY_IMAGE} 3 | 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | ENV DEVEL=1 6 | ENV LANG=C.UTF-8 7 | 8 | RUN apt-get update -qq 9 | RUN apt-get install -yqq --no-install-recommends build-essential chromium less libyaml-dev nano netcat-traditional pkg-config 10 | 11 | ARG BUNDLER_VERSION 12 | RUN gem install bundler -v ${BUNDLER_VERSION} 13 | RUN echo 'gem: --no-document' > /etc/gemrc 14 | 15 | ARG UID 16 | RUN useradd -u $UID --shell /bin/bash app 17 | 18 | RUN mkdir -p /home/app && chown -R app:app /home/app 19 | 20 | ARG RAILS_VERSION 21 | ENV RAILS_VERSION=$RAILS_VERSION 22 | 23 | ARG ACTIVEADMIN_VERSION 24 | ENV ACTIVEADMIN_VERSION=$ACTIVEADMIN_VERSION 25 | 26 | WORKDIR /app 27 | COPY . /app 28 | RUN bundle install 29 | RUN chown -R app:app /usr/local/bundle 30 | 31 | RUN ln -s /app/extra/.bashrc /home/app/.bashrc 32 | -------------------------------------------------------------------------------- /extra/Dockerfile.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything but the required files for bundle install 2 | /**/* 3 | 4 | !/*.gemspec 5 | !/Gemfile 6 | !/lib 7 | -------------------------------------------------------------------------------- /extra/dev_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export DEVEL=1 4 | 5 | export RAILS_VERSION=7.2.2.1 6 | export ACTIVEADMIN_VERSION=3.3.0 7 | -------------------------------------------------------------------------------- /extra/development.md: -------------------------------------------------------------------------------- 1 | ## Development 2 | 3 | ### Dev setup 4 | 5 | There are 2 ways to interact with this project: 6 | 7 | 1) Using Docker: 8 | 9 | ```sh 10 | make up # starts the dev services (optional env vars: RUBY / RAILS / ACTIVEADMIN) 11 | make specs # run the tests (after up) 12 | make lint # run the linters (after up) 13 | make server # run the server (after up) 14 | make shell # open a shell (after up) 15 | make down # cleanup (after up) 16 | 17 | # Example using specific versions: 18 | RUBY=3.2 RAILS=7.1 ACTIVEADMIN=3.2.0 make up 19 | ``` 20 | 21 | 2) With a local setup: 22 | 23 | ```sh 24 | # Dev setup (set the required envs): 25 | source extra/dev_setup.sh 26 | # Install dependencies: 27 | bundle update 28 | # Run server (or any command): 29 | bin/rails s 30 | # To try different versions of Rails/ActiveAdmin edit extra/dev_setup.sh 31 | ``` 32 | -------------------------------------------------------------------------------- /extra/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | build: 4 | context: .. 5 | dockerfile: extra/Dockerfile 6 | args: 7 | BUNDLER_VERSION: ${BUNDLER_VERSION} 8 | RUBY_IMAGE: ruby:${RUBY:-3.4}-slim 9 | RAILS_VERSION: ${RAILS:-} 10 | ACTIVEADMIN_VERSION: ${ACTIVEADMIN:-} 11 | UID: ${UID} 12 | user: ${UID}:${GID} 13 | ports: 14 | - ${SERVER_PORT}:${SERVER_PORT} 15 | working_dir: /app 16 | volumes: 17 | - ..:/app 18 | stdin_open: true 19 | tty: true 20 | -------------------------------------------------------------------------------- /extra/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blocknotes/activeadmin_medium_editor/f6b0f0662c723ceb5af1e574255d400551750897/extra/screenshot.png -------------------------------------------------------------------------------- /lib/activeadmin/medium_editor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'activeadmin/medium_editor/engine' 4 | -------------------------------------------------------------------------------- /lib/activeadmin/medium_editor/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_admin' 4 | 5 | module ActiveAdmin 6 | module MediumEditor 7 | class Engine < ::Rails::Engine 8 | engine_name 'activeadmin_medium_editor' 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/activeadmin/medium_editor/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveAdmin 4 | module MediumEditor 5 | VERSION = '1.0.1' 6 | MEDIUM_VERSION = '5.23.3' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/activeadmin_medium_editor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'activeadmin/medium_editor' 4 | 5 | require 'formtastic/inputs/medium_editor_input' 6 | -------------------------------------------------------------------------------- /lib/formtastic/inputs/medium_editor_input.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Formtastic 4 | module Inputs 5 | class MediumEditorInput < Formtastic::Inputs::TextInput 6 | def input_html_options 7 | super.merge('data-aa-medium-editor': '1') 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/dummy/.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.7.1 2 | -------------------------------------------------------------------------------- /spec/dummy/.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 2.6.6 2 | -------------------------------------------------------------------------------- /spec/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/authors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveAdmin.register Author do 4 | permit_params :name, 5 | :email, 6 | :age, 7 | :avatar, 8 | profile_attributes: %i[id description _destroy], 9 | posts_attributes: %i[id title description] 10 | 11 | index do 12 | selectable_column 13 | id_column 14 | column :name 15 | column :email 16 | column :created_at 17 | actions 18 | end 19 | 20 | filter :name 21 | filter :created_at 22 | 23 | show do 24 | attributes_table do 25 | row :name 26 | row :email 27 | row :age 28 | row :avatar do |record| 29 | image_tag url_for(record.avatar), style: 'max-width:800px;max-height:500px' if record.avatar.attached? 30 | end 31 | row :created_at 32 | row :updated_at 33 | row :profile 34 | row :posts 35 | end 36 | active_admin_comments 37 | end 38 | 39 | form do |f| 40 | f.inputs do 41 | f.input :name 42 | f.input :email 43 | f.input :age 44 | f.input :avatar, 45 | as: :file, 46 | hint: (object.avatar.attached? ? "Current: #{object.avatar.filename}" : nil) 47 | end 48 | f.has_many :profile, allow_destroy: true do |ff| 49 | ff.input :description 50 | end 51 | f.has_many :posts do |fp| 52 | fp.input :title 53 | fp.input :description, as: :medium_editor 54 | end 55 | f.actions 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/dashboard.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register_page "Dashboard" do 2 | menu priority: 1, label: proc { I18n.t("active_admin.dashboard") } 3 | 4 | content title: proc { I18n.t("active_admin.dashboard") } do 5 | div class: "blank_slate_container", id: "dashboard_default_message" do 6 | span class: "blank_slate" do 7 | span I18n.t("active_admin.dashboard_welcome.welcome") 8 | small I18n.t("active_admin.dashboard_welcome.call_to_action") 9 | end 10 | end 11 | 12 | # Here is an example of a simple dashboard with columns and panels. 13 | # 14 | # columns do 15 | # column do 16 | # panel "Recent Posts" do 17 | # ul do 18 | # Post.recent(5).map do |post| 19 | # li link_to(post.title, admin_post_path(post)) 20 | # end 21 | # end 22 | # end 23 | # end 24 | 25 | # column do 26 | # panel "Info" do 27 | # para "Welcome to ActiveAdmin." 28 | # end 29 | # end 30 | # end 31 | end # content 32 | end 33 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/posts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveAdmin.register Post do 4 | permit_params :author_id, :title, :description, :category, :dt, :position, :published, tag_ids: [] 5 | 6 | index do 7 | selectable_column 8 | id_column 9 | column :title 10 | column :author 11 | column :published 12 | column :created_at 13 | actions 14 | end 15 | 16 | show do 17 | attributes_table do 18 | row :author 19 | row :title 20 | row :description 21 | row :category 22 | row :dt 23 | row :position 24 | row :published 25 | row :tags 26 | row :created_at 27 | row :updated_at 28 | end 29 | active_admin_comments 30 | end 31 | 32 | form do |f| 33 | toolbar = { buttons: %w[bold italic underline justifyCenter indent html] } 34 | f.inputs 'Post' do 35 | f.input :author 36 | f.input :title 37 | f.input :description, as: :medium_editor, input_html: { data: { options: { toolbar: toolbar } } } 38 | f.input :category 39 | f.input :dt 40 | f.input :position 41 | f.input :published 42 | end 43 | 44 | f.inputs 'Tags' do 45 | f.input :tags 46 | end 47 | 48 | f.actions 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveAdmin.register Tag do 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | // OFF link active_storage_db_manifest.js 4 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blocknotes/activeadmin_medium_editor/f6b0f0662c723ceb5af1e574255d400551750897/spec/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/active_admin.js: -------------------------------------------------------------------------------- 1 | //= require active_admin/base 2 | 3 | //= require activeadmin/medium_editor/medium_editor 4 | //= require activeadmin/medium_editor_input 5 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/active_admin.scss: -------------------------------------------------------------------------------- 1 | @import 'active_admin/mixins'; 2 | @import 'active_admin/base'; 3 | 4 | @import 'activeadmin/medium_editor/medium_editor'; 5 | @import 'activeadmin/medium_editor_input'; 6 | @import 'activeadmin/medium_editor/themes/default'; // or another theme 7 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/dummy/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blocknotes/activeadmin_medium_editor/f6b0f0662c723ceb5af1e574255d400551750897/spec/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/dummy/app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require activestorage 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /spec/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /spec/dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationRecord < ActiveRecord::Base 4 | self.abstract_class = true 5 | 6 | scope :published, -> {} 7 | end 8 | -------------------------------------------------------------------------------- /spec/dummy/app/models/author.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Author < ApplicationRecord 4 | has_many :posts 5 | has_many :published_posts, -> { published }, class_name: 'Post' 6 | has_many :recent_posts, -> { recents }, class_name: 'Post' 7 | 8 | has_many :tags, through: :posts 9 | 10 | has_one :profile, inverse_of: :author, dependent: :destroy 11 | 12 | has_one_attached :avatar 13 | 14 | accepts_nested_attributes_for :profile, allow_destroy: true 15 | accepts_nested_attributes_for :posts, allow_destroy: true 16 | 17 | validates :email, format: { with: /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\z/i, message: 'Invalid email' } 18 | 19 | validate -> { 20 | errors.add( :base, 'Invalid age' ) if !age || age.to_i % 3 == 1 21 | } 22 | 23 | def to_s 24 | "#{name} (#{age})" 25 | end 26 | 27 | class << self 28 | def ransackable_attributes(_auth_object = nil) 29 | %w[age created_at email id name updated_at] 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blocknotes/activeadmin_medium_editor/f6b0f0662c723ceb5af1e574255d400551750897/spec/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/post.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Post < ApplicationRecord 4 | belongs_to :author, inverse_of: :posts, autosave: true 5 | 6 | has_one :author_profile, through: :author, source: :profile 7 | 8 | has_many :post_tags, inverse_of: :post, dependent: :destroy 9 | has_many :tags, through: :post_tags 10 | 11 | has_many_attached :images 12 | 13 | accepts_nested_attributes_for :post_tags, allow_destroy: true 14 | 15 | validates :title, allow_blank: false, presence: true 16 | 17 | scope :published, -> { where(published: true) } 18 | scope :recents, -> { where('created_at > ?', Date.today - 8.month) } 19 | 20 | def short_title 21 | title.truncate 10 22 | end 23 | 24 | def upper_title 25 | title.upcase 26 | end 27 | 28 | class << self 29 | def ransackable_associations(_auth_object = nil) 30 | %w[author author_profile post_tags tags images_attachments images_blobs] 31 | end 32 | 33 | def ransackable_attributes(_auth_object = nil) 34 | %w[author_id category created_at description dt id position published title summary updated_at] 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/dummy/app/models/post_tag.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class PostTag < ApplicationRecord 4 | belongs_to :post, inverse_of: :post_tags 5 | belongs_to :tag, inverse_of: :post_tags 6 | 7 | validates :post, presence: true 8 | validates :tag, presence: true 9 | 10 | class << self 11 | def ransackable_attributes(auth_object = nil) 12 | %w[created_at id post_id tag_id updated_at] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/dummy/app/models/profile.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Profile < ApplicationRecord 4 | belongs_to :author, inverse_of: :profile, touch: true 5 | 6 | def to_s 7 | description 8 | end 9 | 10 | class << self 11 | def ransackable_associations(_auth_object = nil) 12 | %w[author] 13 | end 14 | 15 | def ransackable_attributes(_auth_object = nil) 16 | %w[author_id created_at description id updated_at] 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/dummy/app/models/tag.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Tag < ApplicationRecord 4 | has_many :post_tags, inverse_of: :tag, dependent: :destroy 5 | has_many :posts, through: :post_tags 6 | 7 | class << self 8 | def ransackable_associations(auth_object = nil) 9 | %w[post_tags posts] 10 | end 11 | 12 | def ransackable_attributes(auth_object = nil) 13 | %w[created_at id id_value name updated_at] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |You may have mistyped the address or the page may have moved.
63 |If you are the application owner check the logs for more information.
65 |Maybe you tried to change something you didn't have access to.
63 |If you are the application owner check the logs for more information.
65 |If you are the application owner check the logs for more information.
64 |