diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebb0e195d4..d4273f7bc7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,35 +12,43 @@ jobs: gemfile: [ gemfiles/rails_6.1.gemfile ] orm: [ active_record ] adapter: [ sqlite3 ] + asset: [ webpacker ] include: - ruby: 2.5 gemfile: gemfiles/rails_6.0.gemfile orm: active_record adapter: sqlite3 + asset: webpacker - ruby: 3.0 gemfile: gemfiles/rails_6.1.gemfile orm: active_record adapter: mysql2 + asset: webpacker - ruby: 3.0 gemfile: gemfiles/rails_6.1.gemfile orm: active_record adapter: postgresql + asset: webpacker - ruby: 3.0 gemfile: gemfiles/rails_7.0.gemfile orm: active_record adapter: sqlite3 + asset: webpacker - ruby: 2.7 gemfile: gemfiles/rails_6.0.gemfile orm: mongoid adapter: sqlite3 + asset: webpacker - ruby: 3.0 gemfile: gemfiles/rails_6.1.gemfile orm: mongoid adapter: sqlite3 + asset: webpacker - ruby: jruby gemfile: gemfiles/rails_6.1.gemfile orm: mongoid adapter: sqlite3 + asset: webpacker runs-on: ubuntu-latest services: mysql: @@ -64,6 +72,7 @@ jobs: env: BUNDLE_GEMFILE: ${{ matrix.gemfile }} CI_ORM: ${{ matrix.orm }} + CI_ASSET: ${{ matrix.asset }} JRUBY_OPTS: --debug steps: - uses: actions/checkout@v2 @@ -76,17 +85,23 @@ jobs: env: MAKE: make --jobs 4 BUNDLE_WITHOUT: development + - name: Set up Node + uses: actions/setup-node@v2 + with: + node-version: '14' - name: Setup application env: BUNDLE_GEMFILE: ../../${{ matrix.gemfile }} CI_DB_ADAPTER: ${{ matrix.adapter }} RAILS_ENV: test run: | + yarn install cd spec/dummy_app bundle exec rake rails_admin:prepare_ci_env db:create db:migrate + yarn install cd ../../ - name: Run tests - run: bundle exec rake spec + run: bundle exec rspec - name: Coveralls Parallel uses: coverallsapp/github-action@master continue-on-error: true diff --git a/.gitignore b/.gitignore index 88aa68691b..7d482925d7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,14 +5,16 @@ .bundle .idea/ .rvmrc +.sass-cache .yardoc /.emacs.desktop /gemfiles/*.lock +/node_modules/* /rails_admin.gems /spec/generators/tmp /spec/lib/tmp +/yarn.lock Gemfile.lock -Gemfile31.lock coverage/* db/*.sqlite3 db/*.sqlite3-journal @@ -26,7 +28,4 @@ spec/dummy_app/log/*.log spec/dummy_app/public/uploads spec/dummy_app/Gemfile.lock tmp/**/* -/.emacs.desktop -.idea/*.xml -.sass-cache nbproject diff --git a/.rspec b/.rspec index 97f3a5d316..af054e7f5c 100644 --- a/.rspec +++ b/.rspec @@ -1,3 +1,4 @@ --color --order=random --profile +--exclude-pattern 'dummy_app/node_modules/rails_admin/**/*_spec.rb' diff --git a/.rubocop.yml b/.rubocop.yml index 5492f39a5c..8e2a87c51c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,11 +3,13 @@ inherit_from: .rubocop_todo.yml AllCops: Exclude: - 'gemfiles/*' + - 'node_modules/**/*' - 'spec/dummy_app/bin/**/*' - 'spec/dummy_app/db/schema.rb' - 'spec/dummy_app/tmp/**/*' - 'vendor/bundle/**/*' NewCops: disable + SuggestExtensions: false TargetRubyVersion: 2.5 Gemspec/DateAssignment: @@ -109,7 +111,7 @@ Metrics/MethodLength: Max: 29 # TODO: Lower to 15 Metrics/ModuleLength: - Max: 202 # TODO: Lower to 100 + Max: 204 # TODO: Lower to 100 Metrics/ParameterLists: Max: 8 # TODO: Lower to 4 diff --git a/Gemfile b/Gemfile index 99204a4112..4dd3bcb8f2 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gem 'rails' gem 'haml' gem 'devise' gem 'webrick', '~> 1.7' +gem 'webpacker', require: false group :active_record do gem 'paper_trail' diff --git a/app/assets/javascripts/rails_admin/custom/ui.js b/app/assets/javascripts/rails_admin/custom/ui.js deleted file mode 100644 index 9d6e5c7cb2..0000000000 --- a/app/assets/javascripts/rails_admin/custom/ui.js +++ /dev/null @@ -1 +0,0 @@ -// override this file in your application to add custom behaviour diff --git a/app/assets/javascripts/rails_admin/jquery-ui.js b/app/assets/javascripts/rails_admin/jquery-ui.js deleted file mode 100644 index 2f31d7f9d9..0000000000 --- a/app/assets/javascripts/rails_admin/jquery-ui.js +++ /dev/null @@ -1,3 +0,0 @@ -//= require 'jquery-ui/effect' -//= require 'jquery-ui/widgets/sortable' -//= require 'jquery-ui/widgets/autocomplete' diff --git a/app/assets/javascripts/rails_admin/ra.i18n.js b/app/assets/javascripts/rails_admin/ra.i18n.js deleted file mode 100644 index ab42470e4e..0000000000 --- a/app/assets/javascripts/rails_admin/ra.i18n.js +++ /dev/null @@ -1,28 +0,0 @@ -(function() { - var Locale; - - this.RailsAdmin || (this.RailsAdmin = {}); - - this.RailsAdmin.I18n = Locale = (function() { - function Locale() {} - - Locale.init = function(locale, translations) { - this.locale = locale; - this.translations = translations; - moment.locale(this.locale); - if (typeof this.translations === "string") { - this.translations = JSON.parse(this.translations); - } - }; - - Locale.t = function(key) { - var humanize; - humanize = key.charAt(0).toUpperCase() + key.replace(/_/g, " ").slice(1); - return this.translations[key] || humanize; - }; - - return Locale; - - })(); - -}).call(this); diff --git a/app/assets/javascripts/rails_admin/rails_admin.js b/app/assets/javascripts/rails_admin/rails_admin.js index 0850ec2a76..8a5586ef0d 100644 --- a/app/assets/javascripts/rails_admin/rails_admin.js +++ b/app/assets/javascripts/rails_admin/rails_admin.js @@ -1,6 +1,5 @@ //= require 'jquery3' //= require 'rails-ujs' -//= require 'jquery.remotipart' //= require 'rails_admin/jquery-ui' //= require 'rails_admin/moment-with-locales' //= require 'rails_admin/bootstrap-datetimepicker' diff --git a/app/assets/stylesheets/rails_admin/aristo/images/bg_fallback.png b/app/assets/stylesheets/rails_admin/aristo/images/bg_fallback.png deleted file mode 100644 index 155ebd7e93..0000000000 Binary files a/app/assets/stylesheets/rails_admin/aristo/images/bg_fallback.png and /dev/null differ diff --git a/app/assets/stylesheets/rails_admin/aristo/images/icon_sprite.png b/app/assets/stylesheets/rails_admin/aristo/images/icon_sprite.png deleted file mode 100644 index 0b376bf66c..0000000000 Binary files a/app/assets/stylesheets/rails_admin/aristo/images/icon_sprite.png and /dev/null differ diff --git a/app/assets/stylesheets/rails_admin/aristo/images/progress_bar.gif b/app/assets/stylesheets/rails_admin/aristo/images/progress_bar.gif deleted file mode 100644 index c3d43fa40b..0000000000 Binary files a/app/assets/stylesheets/rails_admin/aristo/images/progress_bar.gif and /dev/null differ diff --git a/app/assets/stylesheets/rails_admin/aristo/images/slider_handles.png b/app/assets/stylesheets/rails_admin/aristo/images/slider_handles.png deleted file mode 100644 index 872eaac305..0000000000 Binary files a/app/assets/stylesheets/rails_admin/aristo/images/slider_handles.png and /dev/null differ diff --git a/app/assets/stylesheets/rails_admin/aristo/images/ui-icons_222222_256x240.png b/app/assets/stylesheets/rails_admin/aristo/images/ui-icons_222222_256x240.png deleted file mode 100644 index 664494d838..0000000000 Binary files a/app/assets/stylesheets/rails_admin/aristo/images/ui-icons_222222_256x240.png and /dev/null differ diff --git a/app/assets/stylesheets/rails_admin/aristo/images/ui-icons_454545_256x240.png b/app/assets/stylesheets/rails_admin/aristo/images/ui-icons_454545_256x240.png deleted file mode 100644 index 8cccf85760..0000000000 Binary files a/app/assets/stylesheets/rails_admin/aristo/images/ui-icons_454545_256x240.png and /dev/null differ diff --git a/app/assets/stylesheets/rails_admin/custom/mixins.scss b/app/assets/stylesheets/rails_admin/custom/mixins.scss deleted file mode 100644 index 70921182fa..0000000000 --- a/app/assets/stylesheets/rails_admin/custom/mixins.scss +++ /dev/null @@ -1,11 +0,0 @@ -/* - Customize Sass mixins from Twitter-Bootstrap/RailsAdmin theme or add new ones for your own use. - Copy this file to your app/assets/rails_admin/custom/mixins.scss, leave this one untouched - Don't require it in your application.rb - - Available mixins to use/override: - - https://github.com/twbs/bootstrap-sass/tree/master/assets/stylesheets/bootstrap/mixins - https://github.com/railsadminteam/rails_admin/blob/master/app/assets/stylesheets/rails_admin/base/mixins.scss - Plus the ones from your theme. -*/ diff --git a/app/assets/stylesheets/rails_admin/custom/theming.scss b/app/assets/stylesheets/rails_admin/custom/theming.scss deleted file mode 100644 index 72ca4ecae0..0000000000 --- a/app/assets/stylesheets/rails_admin/custom/theming.scss +++ /dev/null @@ -1,13 +0,0 @@ -/* - Customize RailsAdmin theme here. - Copy this file to your app/assets/stylesheets/rails_admin/custom/theming.scss, leave this one untouched - Don't require it in your application.rb - - Look at the markup in RailsAdmin and go there to get inspiration from: - - http://getbootstrap.com - - Test me: (actual color should be the one defined in variables.scss if you did) - - body { background-color: $link-color; } -*/ diff --git a/app/assets/stylesheets/rails_admin/custom/variables.scss b/app/assets/stylesheets/rails_admin/custom/variables.scss deleted file mode 100644 index 4e19047169..0000000000 --- a/app/assets/stylesheets/rails_admin/custom/variables.scss +++ /dev/null @@ -1,15 +0,0 @@ -/* - Customize Sass variables from Twitter-Bootstrap/RailsAdmin theme or add new ones for your own use. - Copy this file to your app/assets/rails_admin/custom/variables.scss, leave this one untouched - Don't require it in your application.rb - - Available variables to use/override: - - https://github.com/twbs/bootstrap-sass/blob/master/assets/stylesheets/bootstrap/_variables.scss - https://github.com/railsadminteam/rails_admin/blob/master/app/assets/stylesheets/rails_admin/base/variables.scss - Plus the ones from your themes. - - Test me: pink links - - $link-color: #F0F; -*/ diff --git a/app/views/layouts/rails_admin/_head.html.haml b/app/views/layouts/rails_admin/_head.html.haml index 12e92abc52..7e9226fd65 100644 --- a/app/views/layouts/rails_admin/_head.html.haml +++ b/app/views/layouts/rails_admin/_head.html.haml @@ -3,5 +3,9 @@ %meta{content: "width=device-width, initial-scale=1", name: "viewport; charset=utf-8"} %meta{content: "NONE,NOARCHIVE", name: "robots"} = csrf_meta_tag -= stylesheet_link_tag "rails_admin/rails_admin.css", media: :all -= javascript_include_tag "rails_admin/rails_admin.js" +- if RailsAdmin::config.asset_source == :webpacker + = javascript_pack_tag "rails_admin" + = stylesheet_pack_tag "rails_admin" +- else + = stylesheet_link_tag "rails_admin/rails_admin.css", media: :all + = javascript_include_tag "rails_admin/rails_admin.js" diff --git a/gemfiles/rails_6.0.gemfile b/gemfiles/rails_6.0.gemfile index 3bad56eedd..0d393372de 100644 --- a/gemfiles/rails_6.0.gemfile +++ b/gemfiles/rails_6.0.gemfile @@ -7,6 +7,7 @@ gem "rails", "~> 6.0.0" gem "haml" gem "devise", "~> 4.7" gem "webrick", "~> 1.7" +gem "webpacker", require: false gem "sassc-rails", "~> 2.1" group :active_record do diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index 6a4d438690..2186a7011e 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -7,6 +7,7 @@ gem "rails", "~> 6.1.0" gem "haml" gem "devise", "~> 4.7" gem "webrick", "~> 1.7" +gem "webpacker", require: false gem "sassc-rails", "~> 2.1" group :active_record do diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index e29e02dc1f..46476a0f8c 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -7,6 +7,7 @@ gem "rails", "~> 7.0.0.alpha2" gem "haml" gem "devise", "~> 4.7", github: "strobilomyces/devise", branch: "patch-1" gem "webrick", "~> 1.7" +gem "webpacker", require: false gem "sassc-rails", "~> 2.1" group :active_record do diff --git a/lib/rails_admin.rb b/lib/rails_admin.rb index bc08f44614..d75c91c88e 100644 --- a/lib/rails_admin.rb +++ b/lib/rails_admin.rb @@ -55,5 +55,3 @@ def self.yaml_dump(object) YAML.dump(object) end end - -require 'rails_admin/bootstrap-sass' unless defined? Bootstrap diff --git a/lib/rails_admin/bootstrap-sass.rb b/lib/rails_admin/bootstrap-sass.rb deleted file mode 100755 index 82fd00730d..0000000000 --- a/lib/rails_admin/bootstrap-sass.rb +++ /dev/null @@ -1,49 +0,0 @@ -module RailsAdmin - module Bootstrap - class FrameworkNotFound < StandardError; end - - # Inspired by Kaminari - def self.load! - if compass? - require 'rails_admin/bootstrap-sass/compass_functions' - register_compass_extension - elsif asset_pipeline? - require 'rails_admin/bootstrap-sass/sass_functions' - end - - require 'sassc-rails' if rails? - - unless rails? || compass? - raise(Bootstrap::FrameworkNotFound.new('bootstrap-sass requires either Rails > 3.1 or Compass, neither of which are loaded')) - end - - if defined?(::Sass) && ::Sass.respond_to?(:load_paths) - stylesheets = File.expand_path(File.join('..', 'vendor', 'assets', 'stylesheets')) - fonts = File.expand_path(File.join('..', 'vendor', 'assets', 'fonts')) - ::Sass.load_paths << stylesheets - ::Sass.load_paths << fonts - end - end - - def self.asset_pipeline? - defined?(::Sprockets) - end - - def self.compass? - defined?(::Compass) - end - - def self.rails? - defined?(::Rails) - end - - def self.register_compass_extension - base = File.join(File.dirname(__FILE__), '..') - styles = File.join(base, 'vendor', 'assets', 'stylesheets') - templates = File.join(base, 'templates') - ::Compass::Frameworks.register('bootstrap', path: base, stylesheets_directory: styles, templates_directory: templates) - end - end -end - -RailsAdmin::Bootstrap.load! diff --git a/lib/rails_admin/bootstrap-sass/compass_functions.rb b/lib/rails_admin/bootstrap-sass/compass_functions.rb deleted file mode 100755 index 3e5ff870fa..0000000000 --- a/lib/rails_admin/bootstrap-sass/compass_functions.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Sass - module Script - module Functions - def image_path(source, _options = {}) - if defined?(::Sprockets) - ::Sass::Script::String.new sprockets_context.image_path(source.value).to_s, :string - elsif defined?(::Compass) - image_url(source, Sass::Script::Bool.new(true)) - else - # Revert to the old compass-agnostic path determination - asset_sans_quotes = source.value.delete('"') - Sass::Script::String.new("/images/#{asset_sans_quotes}", :string) - end - end - - protected - - def sprockets_context # :nodoc: - if options.key?(:sprockets) - options[:sprockets][:context] - else - # Compatibility with sprockets pre 2.10.0 - options[:importer].context - end - end - end - end -end diff --git a/lib/rails_admin/bootstrap-sass/sass_functions.rb b/lib/rails_admin/bootstrap-sass/sass_functions.rb deleted file mode 100755 index 146bc32666..0000000000 --- a/lib/rails_admin/bootstrap-sass/sass_functions.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Sass - module Script - module Functions - # LARS: Snatched from compass - 2011-11-29 - used for gradients in IE6-9 - # returns an IE hex string for a color with an alpha channel - # suitable for passing to IE filters. - def ie_hex_str(color) - assert_type color, :Color - alpha = (color.alpha * 255).round - alphastr = alpha.to_s(16).rjust(2, '0') - Sass::Script::String.new("##{alphastr}#{color.send(:hex_str)[1..-1]}".upcase) - end - declare :ie_hex_str, [:color] if respond_to?(:declare) - end - end -end diff --git a/lib/rails_admin/config.rb b/lib/rails_admin/config.rb index 951daafb00..5d4d3f472c 100644 --- a/lib/rails_admin/config.rb +++ b/lib/rails_admin/config.rb @@ -86,6 +86,9 @@ class << self attr_accessor :navigation_static_links attr_accessor :navigation_static_label + # Set where RailsAdmin fetches JS/CSS from, defaults to :sprockets + attr_accessor :asset_source + # Finish initialization by executing deferred configuration blocks def initialize! @deferred_blocks.each { |block| block.call(self) } @@ -317,6 +320,7 @@ def reset @show_gravatar = true @navigation_static_links = {} @navigation_static_label = nil + @asset_source = (defined?(Webpacker) ? :webpacker : :sprockets) @parent_controller = '::ActionController::Base' @forgery_protection_settings = {with: :exception} RailsAdmin::Config::Actions.reset diff --git a/lib/rails_admin/engine.rb b/lib/rails_admin/engine.rb index 9b2cb2fd6e..821673bec7 100644 --- a/lib/rails_admin/engine.rb +++ b/lib/rails_admin/engine.rb @@ -5,7 +5,6 @@ require 'rack-pjax' require 'rails' require 'rails_admin' -require 'remotipart' module RailsAdmin class Engine < Rails::Engine @@ -14,10 +13,12 @@ class Engine < Rails::Engine config.action_dispatch.rescue_responses['RailsAdmin::ActionNotAllowed'] = :forbidden initializer 'RailsAdmin precompile hook', group: :all do |app| - app.config.assets.precompile += %w( - rails_admin/rails_admin.js - rails_admin/rails_admin.css - ) + if app.config.respond_to?(:assets) + app.config.assets.precompile += %w( + rails_admin/rails_admin.js + rails_admin/rails_admin.css + ) + end end initializer 'RailsAdmin setup middlewares' do |app| diff --git a/package.json b/package.json new file mode 100644 index 0000000000..5478f6d54d --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "dependencies": { + "@fortawesome/fontawesome-free": "^5.15.4", + "@rails/ujs": "^6.1.4-1", + "bootstrap": "3.4.1", + "bootstrap-sass": "3.4.1", + "eonasdan-bootstrap-datetimepicker": "^4.17.49", + "jquery": "^3.6.0", + "jquery-ui": "^1.12.1", + "moment": "^2.29.1" + }, + "directories": { + "src": "./src" + }, + "version": "1.0.0", + "description": "RailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data.", + "name": "rails_admin", + "scripts": { + "link": "yarn link && cd spec/dummy_app && yarn link rails_admin" + } +} diff --git a/rails_admin.gemspec b/rails_admin.gemspec index 696eacf733..1d7c12c622 100644 --- a/rails_admin.gemspec +++ b/rails_admin.gemspec @@ -13,7 +13,6 @@ Gem::Specification.new do |spec| spec.add_dependency 'nested_form', '~> 0.3' spec.add_dependency 'rack-pjax', '>= 0.7' spec.add_dependency 'rails', ['>= 6.0', '< 8'] - spec.add_dependency 'remotipart', '~> 1.3' spec.add_dependency 'sassc-rails', ['>= 1.3', '< 3'] spec.add_dependency 'activemodel-serializers-xml', '>= 1.0' spec.add_development_dependency 'bundler', '>= 1.0' diff --git a/spec/dummy_app/.browserslistrc b/spec/dummy_app/.browserslistrc new file mode 100644 index 0000000000..e94f8140cc --- /dev/null +++ b/spec/dummy_app/.browserslistrc @@ -0,0 +1 @@ +defaults diff --git a/spec/dummy_app/.gitignore b/spec/dummy_app/.gitignore index dd71abbe4e..e0c3904638 100644 --- a/spec/dummy_app/.gitignore +++ b/spec/dummy_app/.gitignore @@ -15,3 +15,11 @@ /tmp /public/system + +/public/packs +/public/packs-test +/node_modules +/yarn-error.log +/yarn.lock +yarn-debug.log* +.yarn-integrity diff --git a/spec/dummy_app/Gemfile b/spec/dummy_app/Gemfile index dbac492daa..c1716537cc 100644 --- a/spec/dummy_app/Gemfile +++ b/spec/dummy_app/Gemfile @@ -1,6 +1,7 @@ source 'https://rubygems.org' gem 'rails', '>= 6.0.0' +gem 'webpacker', require: false group :active_record do platforms :jruby do @@ -30,10 +31,11 @@ gem 'carrierwave', '>= 2.0.0.rc', '< 3.0' gem 'devise', '>= 3.2' gem 'dragonfly', '~> 1.0' gem 'mini_magick', '>= 3.4' -gem 'mlb', '>= 0.7' +gem 'mlb', '>= 0.7', github: 'mshibuya/mlb', branch: 'ruby-3' gem 'paperclip', '>= 3.4' gem 'rails_admin', path: '../../' gem 'shrine', '~> 3.0' +gem 'webrick', '~> 1.7' # Gems used only for assets and not required # in production environments by default. diff --git a/spec/dummy_app/app/assets/javascripts/application.js b/spec/dummy_app/app/assets/javascripts/application.js index 9097d830e2..116e594b8d 100644 --- a/spec/dummy_app/app/assets/javascripts/application.js +++ b/spec/dummy_app/app/assets/javascripts/application.js @@ -10,6 +10,5 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // -//= require jquery -//= require jquery_ujs +//= require rails-ujs //= require_tree . diff --git a/spec/dummy_app/app/javascript/packs/application.js b/spec/dummy_app/app/javascript/packs/application.js new file mode 100644 index 0000000000..7c3021d7ed --- /dev/null +++ b/spec/dummy_app/app/javascript/packs/application.js @@ -0,0 +1,18 @@ +/* eslint no-console:0 */ +// This file is automatically compiled by Webpack, along with any other files +// present in this directory. You're encouraged to place your actual application logic in +// a relevant structure within app/javascript and only use these pack files to reference +// that code so it'll be compiled. +// +// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate +// layout file, like app/views/layouts/application.html.erb + + +// Uncomment to copy all static images under ../images to the output folder and reference +// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) +// or the `imagePath` JavaScript helper below. +// +// const images = require.context('../images', true) +// const imagePath = (name) => images(name, true) + +console.log('Hello World from Webpacker') diff --git a/spec/dummy_app/app/javascript/packs/rails_admin.js b/spec/dummy_app/app/javascript/packs/rails_admin.js new file mode 100644 index 0000000000..470aa39994 --- /dev/null +++ b/spec/dummy_app/app/javascript/packs/rails_admin.js @@ -0,0 +1,2 @@ +import 'rails_admin/src/rails_admin/base' +import '../stylesheets/rails_admin.scss' \ No newline at end of file diff --git a/spec/dummy_app/app/javascript/stylesheets/rails_admin.scss b/spec/dummy_app/app/javascript/stylesheets/rails_admin.scss new file mode 100644 index 0000000000..185a07bb66 --- /dev/null +++ b/spec/dummy_app/app/javascript/stylesheets/rails_admin.scss @@ -0,0 +1 @@ +@import "~rails_admin/src/rails_admin/styles/base.scss"; \ No newline at end of file diff --git a/spec/dummy_app/babel.config.js b/spec/dummy_app/babel.config.js new file mode 100644 index 0000000000..19a07f3146 --- /dev/null +++ b/spec/dummy_app/babel.config.js @@ -0,0 +1,82 @@ +module.exports = function(api) { + var validEnv = ['development', 'test', 'production'] + var currentEnv = api.env() + var isDevelopmentEnv = api.env('development') + var isProductionEnv = api.env('production') + var isTestEnv = api.env('test') + + if (!validEnv.includes(currentEnv)) { + throw new Error( + 'Please specify a valid `NODE_ENV` or ' + + '`BABEL_ENV` environment variables. Valid values are "development", ' + + '"test", and "production". Instead, received: ' + + JSON.stringify(currentEnv) + + '.' + ) + } + + return { + presets: [ + isTestEnv && [ + '@babel/preset-env', + { + targets: { + node: 'current' + } + } + ], + (isProductionEnv || isDevelopmentEnv) && [ + '@babel/preset-env', + { + forceAllTransforms: true, + useBuiltIns: 'entry', + corejs: 3, + modules: false, + exclude: ['transform-typeof-symbol'] + } + ] + ].filter(Boolean), + plugins: [ + 'babel-plugin-macros', + '@babel/plugin-syntax-dynamic-import', + isTestEnv && 'babel-plugin-dynamic-import-node', + '@babel/plugin-transform-destructuring', + [ + '@babel/plugin-proposal-class-properties', + { + loose: true + } + ], + [ + '@babel/plugin-proposal-object-rest-spread', + { + useBuiltIns: true + } + ], + [ + '@babel/plugin-proposal-private-methods', + { + loose: true + } + ], + [ + '@babel/plugin-proposal-private-property-in-object', + { + loose: true + } + ], + [ + '@babel/plugin-transform-runtime', + { + helpers: false + } + ], + [ + '@babel/plugin-transform-regenerator', + { + async: false + } + ] + ].filter(Boolean) + } +} diff --git a/spec/dummy_app/bin/webpack b/spec/dummy_app/bin/webpack new file mode 100755 index 0000000000..1031168d01 --- /dev/null +++ b/spec/dummy_app/bin/webpack @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" +ENV["NODE_ENV"] ||= "development" + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "bundler/setup" + +require "webpacker" +require "webpacker/webpack_runner" + +APP_ROOT = File.expand_path("..", __dir__) +Dir.chdir(APP_ROOT) do + Webpacker::WebpackRunner.run(ARGV) +end diff --git a/spec/dummy_app/bin/webpack-dev-server b/spec/dummy_app/bin/webpack-dev-server new file mode 100755 index 0000000000..dd9662737a --- /dev/null +++ b/spec/dummy_app/bin/webpack-dev-server @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" +ENV["NODE_ENV"] ||= "development" + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "bundler/setup" + +require "webpacker" +require "webpacker/dev_server_runner" + +APP_ROOT = File.expand_path("..", __dir__) +Dir.chdir(APP_ROOT) do + Webpacker::DevServerRunner.run(ARGV) +end diff --git a/spec/dummy_app/config/application.rb b/spec/dummy_app/config/application.rb index e6350d480f..34e0c3e726 100644 --- a/spec/dummy_app/config/application.rb +++ b/spec/dummy_app/config/application.rb @@ -2,7 +2,6 @@ require 'action_controller/railtie' require 'action_mailer/railtie' -require 'sprockets/railtie' begin require CI_ORM.to_s @@ -14,6 +13,13 @@ require 'active_storage/engine' if CI_ORM == :active_record require 'action_text/engine' if CI_ORM == :active_record +case CI_ASSET +when :webpacker + require 'webpacker' +else + require "#{CI_ASSET}/railtie" +end + # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups, CI_ORM) diff --git a/spec/dummy_app/config/boot.rb b/spec/dummy_app/config/boot.rb index 8e888c88d4..48e6a20cc2 100644 --- a/spec/dummy_app/config/boot.rb +++ b/spec/dummy_app/config/boot.rb @@ -1,4 +1,5 @@ CI_ORM = (ENV['CI_ORM'] || :active_record).to_sym unless defined?(CI_ORM) +CI_ASSET = (ENV['CI_ASSET'] || :sprockets).to_sym unless defined?(CI_ASSET) ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/spec/dummy_app/config/environments/development.rb b/spec/dummy_app/config/environments/development.rb index bd8043d9af..2bffc813e4 100644 --- a/spec/dummy_app/config/environments/development.rb +++ b/spec/dummy_app/config/environments/development.rb @@ -31,19 +31,21 @@ # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load if CI_ORM == :active_record - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true - - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true - - # Adds additional error checking when serving assets at runtime. - # Checks for improperly declared sprockets dependencies. - # Raises helpful error messages. - config.assets.raise_runtime_errors = true + if config.respond_to?(:assets) + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. + config.assets.debug = true + + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. + config.assets.digest = true + + # Adds additional error checking when serving assets at runtime. + # Checks for improperly declared sprockets dependencies. + # Raises helpful error messages. + config.assets.raise_runtime_errors = true + end # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/spec/dummy_app/config/initializers/assets.rb b/spec/dummy_app/config/initializers/assets.rb index 01ef3e6630..bdc864b95d 100644 --- a/spec/dummy_app/config/initializers/assets.rb +++ b/spec/dummy_app/config/initializers/assets.rb @@ -1,7 +1,7 @@ # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' +Rails.application.config.assets.version = '1.0' if Rails.application.config.respond_to?(:assets) # Add additional assets to the asset load path # Rails.application.config.assets.paths << Emoji.images_path diff --git a/spec/dummy_app/config/webpack/development.js b/spec/dummy_app/config/webpack/development.js new file mode 100644 index 0000000000..c5edff94ad --- /dev/null +++ b/spec/dummy_app/config/webpack/development.js @@ -0,0 +1,5 @@ +process.env.NODE_ENV = process.env.NODE_ENV || 'development' + +const environment = require('./environment') + +module.exports = environment.toWebpackConfig() diff --git a/spec/dummy_app/config/webpack/environment.js b/spec/dummy_app/config/webpack/environment.js new file mode 100644 index 0000000000..f75acd8462 --- /dev/null +++ b/spec/dummy_app/config/webpack/environment.js @@ -0,0 +1,11 @@ +const webpack = require('webpack'); +const { environment } = require('@rails/webpacker') + +environment.plugins.append( + 'ProvidePlugin-jQuery', + new webpack.ProvidePlugin({ + jQuery: 'jquery', + }) +); + +module.exports = environment diff --git a/spec/dummy_app/config/webpack/production.js b/spec/dummy_app/config/webpack/production.js new file mode 100644 index 0000000000..be0f53aacf --- /dev/null +++ b/spec/dummy_app/config/webpack/production.js @@ -0,0 +1,5 @@ +process.env.NODE_ENV = process.env.NODE_ENV || 'production' + +const environment = require('./environment') + +module.exports = environment.toWebpackConfig() diff --git a/spec/dummy_app/config/webpack/test.js b/spec/dummy_app/config/webpack/test.js new file mode 100644 index 0000000000..c5edff94ad --- /dev/null +++ b/spec/dummy_app/config/webpack/test.js @@ -0,0 +1,5 @@ +process.env.NODE_ENV = process.env.NODE_ENV || 'development' + +const environment = require('./environment') + +module.exports = environment.toWebpackConfig() diff --git a/spec/dummy_app/config/webpacker.yml b/spec/dummy_app/config/webpacker.yml new file mode 100644 index 0000000000..390a6c5d1e --- /dev/null +++ b/spec/dummy_app/config/webpacker.yml @@ -0,0 +1,93 @@ +# Note: You must restart bin/webpack-dev-server for changes to take effect + +default: &default + source_path: app/javascript + source_entry_path: packs + public_root_path: public + public_output_path: packs + cache_path: tmp/cache/webpacker + webpack_compile_output: true + + # Additional paths webpack should lookup modules + # ['app/assets', 'engine/foo/app/assets'] + additional_paths: ['node_modules/rails_admin/src'] + + # Reload manifest.json on all requests so we reload latest compiled packs + cache_manifest: false + + # Extract and emit a css file + extract_css: false + + static_assets_extensions: + - .jpg + - .jpeg + - .png + - .gif + - .tiff + - .ico + - .svg + - .eot + - .otf + - .ttf + - .woff + - .woff2 + + extensions: + - .mjs + - .js + - .sass + - .scss + - .css + - .module.sass + - .module.scss + - .module.css + - .png + - .svg + - .gif + - .jpeg + - .jpg + +development: + <<: *default + compile: true + + # Reference: https://webpack.js.org/configuration/dev-server/ + dev_server: + https: false + host: localhost + port: 3035 + public: localhost:3035 + hmr: false + # Inline should be set to true if using HMR + inline: true + overlay: true + compress: true + disable_host_check: true + use_local_ip: false + quiet: false + pretty: false + headers: + 'Access-Control-Allow-Origin': '*' + watch_options: + ignored: '**/node_modules/**' + + +test: + <<: *default + compile: true + additional_paths: ['src'] # relative from the project root + + # Compile test packs to a separate directory + public_output_path: packs-test + +production: + <<: *default + + # Production depends on precompilation of packs prior to booting for performance. + compile: false + + # Extract and emit a css file + extract_css: true + + # Cache manifest.json for performance + cache_manifest: true diff --git a/spec/dummy_app/package.json b/spec/dummy_app/package.json new file mode 100644 index 0000000000..d849b38f48 --- /dev/null +++ b/spec/dummy_app/package.json @@ -0,0 +1,14 @@ +{ + "name": "dummy_app", + "private": true, + "version": "0.1.0", + "dependencies": { + "@rails/webpacker": "5.4.3", + "webpack": "^4.46.0", + "webpack-cli": "^3.3.12", + "rails_admin": "file:../../" + }, + "devDependencies": { + "webpack-dev-server": "^3" + } +} diff --git a/spec/dummy_app/postcss.config.js b/spec/dummy_app/postcss.config.js new file mode 100644 index 0000000000..aa5998a809 --- /dev/null +++ b/spec/dummy_app/postcss.config.js @@ -0,0 +1,12 @@ +module.exports = { + plugins: [ + require('postcss-import'), + require('postcss-flexbugs-fixes'), + require('postcss-preset-env')({ + autoprefixer: { + flexbox: 'no-2009' + }, + stage: 3 + }) + ] +} diff --git a/spec/integration/rails_admin_spec.rb b/spec/integration/rails_admin_spec.rb index a4bc13ba1a..a4168a5c79 100644 --- a/spec/integration/rails_admin_spec.rb +++ b/spec/integration/rails_admin_spec.rb @@ -31,11 +31,21 @@ # Note: the [href^="/asset... syntax matches the start of a value. The reason # we just do that is to avoid being confused by rails' asset_ids. it 'loads stylesheets in header' do - is_expected.to have_selector('head link[href^="/assets/rails_admin/rails_admin"][href$=".css"]', visible: false) + case RailsAdmin.config.asset_source + when :sprockets + is_expected.to have_selector('head link[href^="/assets/rails_admin/rails_admin"][href$=".css"]', visible: false) + when :webpacker + is_expected.to have_no_selector('head link[href~="rails_admin"][href$=".css"]', visible: false) + end end it 'loads javascript files in body' do - is_expected.to have_selector('head script[src^="/assets/rails_admin/rails_admin"][src$=".js"]', visible: false) + case RailsAdmin.config.asset_source + when :sprockets + is_expected.to have_selector('head script[src^="/assets/rails_admin/rails_admin"][src$=".js"]', visible: false) + when :webpacker + is_expected.to have_selector('head script[src^="/packs-test/js/rails_admin"][src$=".js"]', visible: false) + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 05b0619d18..41c13f805a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -96,6 +96,10 @@ def password_digest(password) Capybara.reset! if example.metadata[:js] end + config.before(:all) do + Webpacker.instance.compiler.compile if defined?(Webpacker) + end + config.before do |example| DatabaseCleaner.strategy = (CI_ORM == :mongoid || example.metadata[:js]) ? :deletion : :transaction diff --git a/src/rails_admin/base.js b/src/rails_admin/base.js new file mode 100644 index 0000000000..32b9eb3353 --- /dev/null +++ b/src/rails_admin/base.js @@ -0,0 +1,21 @@ +import Rails from '@rails/ujs' +import jQuery from 'jquery' +import moment from 'moment' +import './vendor/jquery.pjax' +import './vendor/jquery_nested_form' +import 'bootstrap' +import 'moment/min/locales.js' +import 'eonasdan-bootstrap-datetimepicker' + +import './filter-box' +import './filtering-multiselect' +import './filtering-select' +import './nested-form-hooks' +import './remote-form' +import './sidescroll' +import './ui' +import './widgets' + +Rails.start(); +window.$ = window.jQuery = jQuery; +window.moment = moment; \ No newline at end of file diff --git a/app/assets/javascripts/rails_admin/ra.filter-box.js b/src/rails_admin/filter-box.js similarity index 86% rename from app/assets/javascripts/rails_admin/ra.filter-box.js rename to src/rails_admin/filter-box.js index 8439a218bf..c7584c05e3 100644 --- a/app/assets/javascripts/rails_admin/ra.filter-box.js +++ b/src/rails_admin/filter-box.js @@ -1,3 +1,7 @@ +import jQuery from 'jquery'; +import moment from 'moment' +import I18n from './i18n'; + (function($) { var filters; @@ -23,13 +27,13 @@ control = $('') .prop('name', value_name) .append('') - .append($('').prop('selected', field_value == "true").text(RailsAdmin.I18n.t("true"))) - .append($('').prop('selected', field_value == "false").text(RailsAdmin.I18n.t("false"))) + .append($('').prop('selected', field_value == "true").text(I18n.t("true"))) + .append($('').prop('selected', field_value == "false").text(I18n.t("false"))) if (!required) { control.append([ '', - $('').prop('selected', field_value == "_present").text(RailsAdmin.I18n.t("is_present")), - $('').prop('selected', field_value == "_blank").text(RailsAdmin.I18n.t("is_blank")) + $('').prop('selected', field_value == "_present").text(I18n.t("is_present")), + $('').prop('selected', field_value == "_blank").text(I18n.t("is_blank")) ]) } break; @@ -39,21 +43,21 @@ case 'time': control = control || $('') .prop('name', operator_name) - .append($('').prop('selected', field_operator == "default").text(RailsAdmin.I18n.t(field_type == "time" ? "time" : "date"))) - .append($('').prop('selected', field_operator == "between").text(RailsAdmin.I18n.t("between_and_"))) + .append($('').prop('selected', field_operator == "default").text(I18n.t(field_type == "time" ? "time" : "date"))) + .append($('').prop('selected', field_operator == "between").text(I18n.t("between_and_"))) if (field_type != 'time') { control.append([ - $('').prop('selected', field_operator == "today").text(RailsAdmin.I18n.t("today")), - $('').prop('selected', field_operator == "yesterday").text(RailsAdmin.I18n.t("yesterday")), - $('').prop('selected', field_operator == "this_week").text(RailsAdmin.I18n.t("this_week")), - $('').prop('selected', field_operator == "last_week").text(RailsAdmin.I18n.t("last_week")), + $('').prop('selected', field_operator == "today").text(I18n.t("today")), + $('').prop('selected', field_operator == "yesterday").text(I18n.t("yesterday")), + $('').prop('selected', field_operator == "this_week").text(I18n.t("this_week")), + $('').prop('selected', field_operator == "last_week").text(I18n.t("last_week")), ]) } if (!required) { control.append([ '', - $('').prop('selected', field_operator == "_not_null").text(RailsAdmin.I18n.t("is_present")), - $('').prop('selected', field_operator == "_null").text(RailsAdmin.I18n.t("is_blank")) + $('').prop('selected', field_operator == "_not_null").text(I18n.t("is_present")), + $('').prop('selected', field_operator == "_null").text(I18n.t("is_blank")) ]) } additional_control = @@ -83,8 +87,8 @@ .data('name', value_name) .append('') .append(required ? [] : [ - $('').prop('selected', field_value == "_present").text(RailsAdmin.I18n.t("is_present")), - $('').prop('selected', field_value == "_blank").text(RailsAdmin.I18n.t("is_blank")), + $('').prop('selected', field_value == "_present").text(I18n.t("is_present")), + $('').prop('selected', field_value == "_blank").text(I18n.t("is_blank")), '' ]) .append(select_options) @@ -107,15 +111,15 @@ .prop('value', field_operator) .prop('name', operator_name) .append('') - .append($('').prop('selected', field_operator == "like").text(RailsAdmin.I18n.t("contains"))) - .append($('').prop('selected', field_operator == "is").text(RailsAdmin.I18n.t("is_exactly"))) - .append($('').prop('selected', field_operator == "starts_with").text(RailsAdmin.I18n.t("starts_with"))) - .append($('').prop('selected', field_operator == "ends_with").text(RailsAdmin.I18n.t("ends_with"))) + .append($('').prop('selected', field_operator == "like").text(I18n.t("contains"))) + .append($('').prop('selected', field_operator == "is").text(I18n.t("is_exactly"))) + .append($('').prop('selected', field_operator == "starts_with").text(I18n.t("starts_with"))) + .append($('').prop('selected', field_operator == "ends_with").text(I18n.t("ends_with"))) if (!required) { control.append([ '', - $('').prop('selected', field_operator == "_present").text(RailsAdmin.I18n.t("is_present")), - $('').prop('selected', field_operator == "_blank").text(RailsAdmin.I18n.t("is_blank")) + $('').prop('selected', field_operator == "_present").text(I18n.t("is_present")), + $('').prop('selected', field_operator == "_blank").text(I18n.t("is_blank")) ]) } additional_control = $('') @@ -128,13 +132,13 @@ case 'float': control = $('') .prop('name', operator_name) - .append($('').prop('selected', field_operator == "default").text(RailsAdmin.I18n.t("number"))) - .append($('').prop('selected', field_operator == "between").text(RailsAdmin.I18n.t("between_and_"))) + .append($('').prop('selected', field_operator == "default").text(I18n.t("number"))) + .append($('').prop('selected', field_operator == "between").text(I18n.t("between_and_"))) if (!required) { control.append([ '', - $('').prop('selected', field_operator == "_not_null").text(RailsAdmin.I18n.t("is_present")), - $('').prop('selected', field_operator == "_null").text(RailsAdmin.I18n.t("is_blank")) + $('').prop('selected', field_operator == "_not_null").text(I18n.t("is_present")), + $('').prop('selected', field_operator == "_null").text(I18n.t("is_blank")) ]) } additional_control = @@ -184,7 +188,7 @@ $content.find('.date, .datetime').each(function() { $(this).datetimepicker({ date: moment($(this).siblings('[type=hidden]').val()), - locale: RailsAdmin.I18n.locale, + locale: I18n.locale, showTodayButton: true, format: options['datetimepicker_format'] }); @@ -216,7 +220,7 @@ $(document).on('click', "#filters_box .delete", function(e) { e.preventDefault(); - form = $(this).parents('form'); + var form = $(this).parents('form'); $(this).parents('.filter').remove(); !$("#filters_box").children().length && $("hr.filters_box:visible").hide('slow'); }); @@ -232,7 +236,8 @@ $(document).on('change', "#filters_box .switch-additional-fieldsets", function(e) { var selected_option = $(this).find('option:selected'); - if(klass = $(selected_option).data('additional-fieldset')) { + var klass = $(selected_option).data('additional-fieldset'); + if(klass) { $(this).siblings('.additional-fieldset:not(.' + klass + ')').hide('slow'); $(this).siblings('.' + klass).show('slow'); } else { diff --git a/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js b/src/rails_admin/filtering-multiselect.js similarity index 97% rename from app/assets/javascripts/rails_admin/ra.filtering-multiselect.js rename to src/rails_admin/filtering-multiselect.js index 34350740e1..08b3d2cc43 100644 --- a/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js +++ b/src/rails_admin/filtering-multiselect.js @@ -1,14 +1,7 @@ -/* - * RailsAdmin filtering multiselect @VERSION - * - * License - * - * http://www.railsadmin.org - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ +import jQuery from 'jquery'; +import 'jquery-ui/ui/widget'; +import I18n from './i18n'; + (function($) { $.widget("ra.filteringMultiselect", { _cache: {}, @@ -120,8 +113,8 @@ this.element.css({display: "none"}); - this.tooManyObjectsPlaceholder = $('').text(RailsAdmin.I18n.t("too_many_objects")); - this.noObjectsPlaceholder = $('').text(RailsAdmin.I18n.t("no_objects")) + this.tooManyObjectsPlaceholder = $('').text(I18n.t("too_many_objects")); + this.noObjectsPlaceholder = $('').text(I18n.t("no_objects")) if (this.options.xhr) { this.collection.append(this.tooManyObjectsPlaceholder); @@ -254,7 +247,7 @@ if (!this.options.xhr) { for (i in this._cache) { if (this._cache.hasOwnProperty(i)) { - option = this._cache[i]; + var option = this._cache[i]; matches.push({id: option.id, label: option.value}); } } @@ -282,7 +275,7 @@ for (i in this._cache) { if (this._cache.hasOwnProperty(i) && query.test(this._cache[i]['value'])) { - option = this._cache[i]; + var option = this._cache[i]; matches.push({id: option.id, label: option.value}); } } diff --git a/app/assets/javascripts/rails_admin/ra.filtering-select.js b/src/rails_admin/filtering-select.js similarity index 95% rename from app/assets/javascripts/rails_admin/ra.filtering-select.js rename to src/rails_admin/filtering-select.js index b292efb51e..b7075d1056 100644 --- a/app/assets/javascripts/rails_admin/ra.filtering-select.js +++ b/src/rails_admin/filtering-select.js @@ -1,18 +1,8 @@ -/* - * RailsAdmin filtering select @VERSION - * - * Based on the combobox example from jQuery UI documentation - * http://jqueryui.com/demos/autocomplete/#combobox - * - * License - * - * http://www.railsadmin.org - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.autocomplete.js - */ +import jQuery from 'jquery'; +import 'jquery-ui/ui/widget'; +import 'jquery-ui/ui/widgets/autocomplete'; +import I18n from './i18n'; + (function($) { 'use strict'; @@ -51,9 +41,9 @@ this.button = this._buttonField(); } this.clearOption = $('').append( - ' ' + $('').text(RailsAdmin.I18n.t("clear")).html() + ' ' + $('').text(I18n.t("clear")).html() ); - this.noObjectsPlaceholder = $('').text(RailsAdmin.I18n.t("no_objects")); + this.noObjectsPlaceholder = $('').text(I18n.t("no_objects")); this._setOptionsSource(); this._initAutocomplete(); diff --git a/src/rails_admin/i18n.js b/src/rails_admin/i18n.js new file mode 100644 index 0000000000..5275723b6f --- /dev/null +++ b/src/rails_admin/i18n.js @@ -0,0 +1,19 @@ +import moment from 'moment' + +export default { + locale: null, + translations: null, + init(locale, translations) { + this.locale = locale; + this.translations = translations; + moment.locale(this.locale); + if (typeof this.translations === "string") { + this.translations = JSON.parse(this.translations); + } + }, + t(key) { + var humanize; + humanize = key.charAt(0).toUpperCase() + key.replace(/_/g, " ").slice(1); + return this.translations[key] || humanize; + } +}; diff --git a/app/assets/images/rails_admin/aristo/images/bg_fallback.png b/src/rails_admin/images/aristo/images/bg_fallback.png similarity index 100% rename from app/assets/images/rails_admin/aristo/images/bg_fallback.png rename to src/rails_admin/images/aristo/images/bg_fallback.png diff --git a/app/assets/images/rails_admin/aristo/images/icon_sprite.png b/src/rails_admin/images/aristo/images/icon_sprite.png similarity index 100% rename from app/assets/images/rails_admin/aristo/images/icon_sprite.png rename to src/rails_admin/images/aristo/images/icon_sprite.png diff --git a/app/assets/images/rails_admin/aristo/images/progress_bar.gif b/src/rails_admin/images/aristo/images/progress_bar.gif similarity index 100% rename from app/assets/images/rails_admin/aristo/images/progress_bar.gif rename to src/rails_admin/images/aristo/images/progress_bar.gif diff --git a/app/assets/images/rails_admin/aristo/images/slider_handles.png b/src/rails_admin/images/aristo/images/slider_handles.png similarity index 100% rename from app/assets/images/rails_admin/aristo/images/slider_handles.png rename to src/rails_admin/images/aristo/images/slider_handles.png diff --git a/app/assets/images/rails_admin/aristo/images/ui-icons_222222_256x240.png b/src/rails_admin/images/aristo/images/ui-icons_222222_256x240.png similarity index 100% rename from app/assets/images/rails_admin/aristo/images/ui-icons_222222_256x240.png rename to src/rails_admin/images/aristo/images/ui-icons_222222_256x240.png diff --git a/app/assets/images/rails_admin/aristo/images/ui-icons_454545_256x240.png b/src/rails_admin/images/aristo/images/ui-icons_454545_256x240.png similarity index 100% rename from app/assets/images/rails_admin/aristo/images/ui-icons_454545_256x240.png rename to src/rails_admin/images/aristo/images/ui-icons_454545_256x240.png diff --git a/app/assets/images/rails_admin/bullet_black.png b/src/rails_admin/images/bullet_black.png similarity index 100% rename from app/assets/images/rails_admin/bullet_black.png rename to src/rails_admin/images/bullet_black.png diff --git a/app/assets/images/rails_admin/bullet_white.png b/src/rails_admin/images/bullet_white.png similarity index 100% rename from app/assets/images/rails_admin/bullet_white.png rename to src/rails_admin/images/bullet_white.png diff --git a/app/assets/images/rails_admin/calendar.png b/src/rails_admin/images/calendar.png similarity index 100% rename from app/assets/images/rails_admin/calendar.png rename to src/rails_admin/images/calendar.png diff --git a/app/assets/images/rails_admin/clock.png b/src/rails_admin/images/clock.png similarity index 100% rename from app/assets/images/rails_admin/clock.png rename to src/rails_admin/images/clock.png diff --git a/app/assets/images/rails_admin/logo.png b/src/rails_admin/images/logo.png similarity index 100% rename from app/assets/images/rails_admin/logo.png rename to src/rails_admin/images/logo.png diff --git a/app/assets/images/rails_admin/magnifier.png b/src/rails_admin/images/magnifier.png similarity index 100% rename from app/assets/images/rails_admin/magnifier.png rename to src/rails_admin/images/magnifier.png diff --git a/app/assets/images/rails_admin/multiselect/icon_sprite.png b/src/rails_admin/images/multiselect/icon_sprite.png similarity index 100% rename from app/assets/images/rails_admin/multiselect/icon_sprite.png rename to src/rails_admin/images/multiselect/icon_sprite.png diff --git a/app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-n-dark.png b/src/rails_admin/images/multiselect/ui-icon-circle-triangle-n-dark.png similarity index 100% rename from app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-n-dark.png rename to src/rails_admin/images/multiselect/ui-icon-circle-triangle-n-dark.png diff --git a/app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-n-light.png b/src/rails_admin/images/multiselect/ui-icon-circle-triangle-n-light.png similarity index 100% rename from app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-n-light.png rename to src/rails_admin/images/multiselect/ui-icon-circle-triangle-n-light.png diff --git a/app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-s-dark.png b/src/rails_admin/images/multiselect/ui-icon-circle-triangle-s-dark.png similarity index 100% rename from app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-s-dark.png rename to src/rails_admin/images/multiselect/ui-icon-circle-triangle-s-dark.png diff --git a/app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-s-light.png b/src/rails_admin/images/multiselect/ui-icon-circle-triangle-s-light.png similarity index 100% rename from app/assets/images/rails_admin/multiselect/ui-icon-circle-triangle-s-light.png rename to src/rails_admin/images/multiselect/ui-icon-circle-triangle-s-light.png diff --git a/app/assets/javascripts/rails_admin/ra.nested-form-hooks.js b/src/rails_admin/nested-form-hooks.js similarity index 98% rename from app/assets/javascripts/rails_admin/ra.nested-form-hooks.js rename to src/rails_admin/nested-form-hooks.js index 93398894b3..65cde972bf 100644 --- a/app/assets/javascripts/rails_admin/ra.nested-form-hooks.js +++ b/src/rails_admin/nested-form-hooks.js @@ -1,3 +1,5 @@ +import jQuery from 'jquery'; + (function($) { $(document).ready(function() { return window.nestedFormEvents.insertFields = function(content, assoc, link) { diff --git a/app/assets/javascripts/rails_admin/ra.remote-form.js b/src/rails_admin/remote-form.js similarity index 94% rename from app/assets/javascripts/rails_admin/ra.remote-form.js rename to src/rails_admin/remote-form.js index cce88d3c15..7423c0ecd2 100644 --- a/app/assets/javascripts/rails_admin/ra.remote-form.js +++ b/src/rails_admin/remote-form.js @@ -1,20 +1,12 @@ -/* - * RailsAdmin remote form @VERSION - * - * License - * - * http://www.railsadmin.org - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.dialog.js - */ +import Rails from '@rails/ujs' +import jQuery from 'jquery'; +import 'jquery-ui/ui/widget'; + (function($) { $.widget("ra.remoteForm", { _create: function() { - var widget = this + var widget = this; var dom_widget = widget.element; var edit_url = dom_widget.find('select').first().data('options') && dom_widget.find('select').first().data('options')['edit-url']; @@ -29,7 +21,8 @@ }); dom_widget.find('.update').unbind().bind("click", function(e){ - if(value = dom_widget.find('select').val()) { + var value = dom_widget.find('select').val(); + if(value) { widget._bindModalOpening(e, $(this).data('link').replace('__ID__', value)) } else { e.preventDefault(); @@ -39,7 +32,7 @@ _bindModalOpening: function(e, url) { e.preventDefault(); - widget = this; + var widget = this; if($("#modal").length) return false; @@ -80,7 +73,7 @@ }).html(cancelButtonText); dialog.find('.save-action').unbind().click(function(){ - window.Rails.fire(form[0], 'submit'); + Rails.fire(form[0], 'submit'); return false; }).html(saveButtonText); diff --git a/app/assets/javascripts/rails_admin/ra.sidescroll.js b/src/rails_admin/sidescroll.js similarity index 96% rename from app/assets/javascripts/rails_admin/ra.sidescroll.js rename to src/rails_admin/sidescroll.js index 7dfe516a8f..26c9d197ec 100644 --- a/app/assets/javascripts/rails_admin/ra.sidescroll.js +++ b/src/rails_admin/sidescroll.js @@ -1,3 +1,5 @@ +import jQuery from 'jquery'; + (function($) { function setFrozenColPositions() { var $listForm, frozenColumns; diff --git a/app/assets/stylesheets/rails_admin/aristo/jquery-ui-1.8.7.custom.scss b/src/rails_admin/styles/aristo/jquery-ui-1.8.7.custom.scss similarity index 94% rename from app/assets/stylesheets/rails_admin/aristo/jquery-ui-1.8.7.custom.scss rename to src/rails_admin/styles/aristo/jquery-ui-1.8.7.custom.scss index 7c539a0529..0c0cdee580 100644 --- a/app/assets/stylesheets/rails_admin/aristo/jquery-ui-1.8.7.custom.scss +++ b/src/rails_admin/styles/aristo/jquery-ui-1.8.7.custom.scss @@ -63,7 +63,7 @@ .ui-widget-content a { color: #4F4F4F; } .ui-widget-header { border: 1px solid #B6B6B6; color: #4F4F4F; font-weight: bold; } .ui-widget-header { - background: #ededed image-url('rails_admin/aristo/images/bg_fallback.png') 0 0 repeat-x; /* Old browsers */ + background: #ededed url('~rails_admin/src/rails_admin/images/aristo/images/bg_fallback.png') 0 0 repeat-x; /* Old browsers */ background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ @@ -77,7 +77,7 @@ ----------------------------------*/ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #B6B6B6; font-weight: normal; color: #4F4F4F; } .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { - background: #ededed image-url('rails_admin/aristo/images/bg_fallback.png') 0 0 repeat-x; /* Old browsers */ + background: #ededed url('~rails_admin/src/rails_admin/images/aristo/images/bg_fallback.png') 0 0 repeat-x; /* Old browsers */ background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */ @@ -94,7 +94,7 @@ .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { outline: none; color: #1c4257; border: 1px solid #7096ab; - background: #ededed image-url('rails_admin/aristo/images/bg_fallback.png') 0 -50px repeat-x; /* Old browsers */ + background: #ededed url('~rails_admin/src/rails_admin/images/aristo/images/bg_fallback.png') 0 -50px repeat-x; /* Old browsers */ background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ @@ -123,14 +123,14 @@ ----------------------------------*/ /* states and images */ -.ui-icon { width: 16px; height: 16px; background-image: image-url('rails_admin/aristo/images/ui-icons_222222_256x240.png'); } -.ui-widget-content .ui-icon {background-image: image-url('rails_admin/aristo/images/ui-icons_222222_256x240.png'); } -.ui-widget-header .ui-icon {background-image: image-url('rails_admin/aristo/images/ui-icons_222222_256x240.png'); } -.ui-state-default .ui-icon { background-image: image-url('rails_admin/aristo/images/ui-icons_454545_256x240.png'); } -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: image-url('rails_admin/aristo/images/ui-icons_454545_256x240.png'); } -.ui-state-active .ui-icon {background-image: image-url('rails_admin/aristo/images/ui-icons_454545_256x240.png'); } -.ui-state-highlight .ui-icon {background-image: image-url('rails_admin/aristo/images/ui-icons_454545_256x240.png'); } -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: image-url('rails_admin/aristo/images/icon_sprite.png') -16px 0 no-repeat !important; } +.ui-icon { width: 16px; height: 16px; background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_222222_256x240.png'); } +.ui-widget-content .ui-icon {background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_222222_256x240.png'); } +.ui-widget-header .ui-icon {background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_222222_256x240.png'); } +.ui-state-default .ui-icon { background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_454545_256x240.png'); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_454545_256x240.png'); } +.ui-state-active .ui-icon {background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_454545_256x240.png'); } +.ui-state-highlight .ui-icon {background-image: url('~rails_admin/src/rails_admin/images/aristo/images/ui-icons_454545_256x240.png'); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url('~rails_admin/src/rails_admin/images/aristo/images/icon_sprite.png') -16px 0 no-repeat !important; } .ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin-top: -1px; } /* positioning */ @@ -255,7 +255,7 @@ .ui-icon-video { background-position: -224px -128px; } .ui-icon-script { background-position: -240px -128px; } .ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background: image-url('rails_admin/aristo/images/icon_sprite.png') 0 0 no-repeat !important; } +.ui-icon-info { background: url('~rails_admin/src/rails_admin/images/aristo/images/icon_sprite.png') 0 0 no-repeat !important; } .ui-icon-notice { background-position: -32px -144px; } .ui-icon-help { background-position: -48px -144px; } .ui-icon-check { background-position: -64px -144px; } @@ -475,7 +475,7 @@ button.ui-button-icons-only { width: 3.7em; } outline: none; color: #1c4257; border-color: #7096ab; - background: #ededed image-url('rails_admin/aristo/images/bg_fallback.png') 0 -50px repeat-x; /* Old browsers */ + background: #ededed url('~rails_admin/src/rails_admin/images/aristo/images/bg_fallback.png') 0 -50px repeat-x; /* Old browsers */ background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ @@ -521,7 +521,7 @@ input.ui-button::-moz-focus-inner { .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } .ui-buttonset .ui-button.ui-state-active { color: #1c4257; border-color: #7096ab; } .ui-buttonset .ui-button.ui-state-active { - background: #ededed image-url('rails_admin/aristo/images/bg_fallback.png') 0 -50px repeat-x; /* Old browsers */ + background: #ededed url('~rails_admin/src/rails_admin/images/aristo/images/bg_fallback.png') 0 -50px repeat-x; /* Old browsers */ background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */ @@ -552,7 +552,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad .ui-dialog .ui-dialog-titlebar { padding: 0.7em 1em 0.6em 1em; position: relative; border: none; border-bottom: 1px solid #979797; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; font-size: 14px; text-shadow: 0 1px 0 rgba(255,255,255,0.5); } .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .8em; top: 55%; width: 16px; margin: -10px 0 0 0; padding: 0; height: 16px; } -.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; background: image-url('rails_admin/aristo/images/icon_sprite.png') 0 -16px no-repeat; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; background: url('~rails_admin/src/rails_admin/images/aristo/images/icon_sprite.png') 0 -16px no-repeat; } .ui-dialog .ui-dialog-titlebar-close:hover span { background-position: -16px -16px; } .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; border: 0; } .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } @@ -572,7 +572,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad */ .ui-slider { position: relative; text-align: left; background: #d7d7d7; z-index: 1; } .ui-slider { -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; } -.ui-slider .ui-slider-handle { background: image-url('rails_admin/aristo/images/slider_handles.png') 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: default; border: none; outline: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } +.ui-slider .ui-slider-handle { background: url('~rails_admin/src/rails_admin/images/aristo/images/slider_handles.png') 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: default; border: none; outline: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } .ui-slider .ui-state-hover, .ui-slider .ui-state-active { background-position: 0 0; } .ui-slider .ui-slider-range { background: #a3cae0; position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } .ui-slider .ui-slider-range { -moz-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; -webkit-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; } @@ -640,7 +640,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad .ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; } .ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; } .ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; } -.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: image-url('rails_admin/aristo/images/icon_sprite.png') no-repeat; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: url('~rails_admin/src/rails_admin/images/aristo/images/icon_sprite.png') no-repeat; } .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; font-size: 12px; text-shadow: 0 1px 0 rgba(255,255,255,0.6); } .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } .ui-datepicker select.ui-datepicker-month-year {width: 100%;} @@ -701,8 +701,8 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad * * http://docs.jquery.com/UI/Progressbar#theming */ -.ui-progressbar { height: 12px; text-align: left; background: #FFF image-url('rails_admin/aristo/images/progress_bar.gif') 0 -14px repeat-x; } -.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; background: image-url('rails_admin/aristo/images/progress_bar.gif') 0 0 repeat-x; } +.ui-progressbar { height: 12px; text-align: left; background: #FFF url('~rails_admin/src/rails_admin/images/aristo/images/progress_bar.gif') 0 -14px repeat-x; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; background: url('~rails_admin/src/rails_admin/images/aristo/images/progress_bar.gif') 0 0 repeat-x; } /* Extra Input Field Styling */ .ui-form textarea, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]) { diff --git a/src/rails_admin/styles/base.scss b/src/rails_admin/styles/base.scss new file mode 100644 index 0000000000..160eac4ef7 --- /dev/null +++ b/src/rails_admin/styles/base.scss @@ -0,0 +1,69 @@ +@charset "UTF-8"; + +/*** Variables ***/ + +@import "~bootstrap-sass/assets/stylesheets/bootstrap/_variables"; +@import "base/variables"; +@import "themes/default/variables"; + +/*** Mixins ***/ + +@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins"; +@import "base/mixins"; +@import "themes/default/mixins"; + +/*** Libraries ***/ + +@import "./aristo/jquery-ui-1.8.7.custom"; +@import "~eonasdan-bootstrap-datetimepicker/src/sass/bootstrap-datetimepicker-build"; +@import "./filtering-multiselect"; +@import "./widgets"; +@import "./sidescroll"; + + +/*** Font-awesome ***/ + +$fa-font-path: '~@fortawesome/fontawesome-free/webfonts'; +@import '~@fortawesome/fontawesome-free/scss/solid'; +@import '~@fortawesome/fontawesome-free/scss/fontawesome'; + +/*** Bootstrap Theming ***/ + +@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/type"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/forms"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/component-animations"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/close"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/buttons"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/button-groups"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/input-groups"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/navs"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/navbar"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/pagination"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/pager"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/modals"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/tooltip"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/popovers"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/thumbnails"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/labels"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/panels"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/badges"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/progress-bars"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/carousel"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities"; +@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities"; + +/*** RailsAdmin Theming ***/ + +@import "base/theming"; +@import "themes/default/theming"; + + + diff --git a/app/assets/stylesheets/rails_admin/base/README.txt b/src/rails_admin/styles/base/README.txt similarity index 100% rename from app/assets/stylesheets/rails_admin/base/README.txt rename to src/rails_admin/styles/base/README.txt diff --git a/app/assets/stylesheets/rails_admin/base/mixins.scss b/src/rails_admin/styles/base/mixins.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/base/mixins.scss rename to src/rails_admin/styles/base/mixins.scss diff --git a/app/assets/stylesheets/rails_admin/base/theming.scss b/src/rails_admin/styles/base/theming.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/base/theming.scss rename to src/rails_admin/styles/base/theming.scss diff --git a/app/assets/stylesheets/rails_admin/base/variables.scss b/src/rails_admin/styles/base/variables.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/base/variables.scss rename to src/rails_admin/styles/base/variables.scss diff --git a/app/assets/stylesheets/rails_admin/ra.filtering-multiselect.scss b/src/rails_admin/styles/filtering-multiselect.scss similarity index 65% rename from app/assets/stylesheets/rails_admin/ra.filtering-multiselect.scss rename to src/rails_admin/styles/filtering-multiselect.scss index e796076535..2bd8ae1901 100644 --- a/app/assets/stylesheets/rails_admin/ra.filtering-multiselect.scss +++ b/src/rails_admin/styles/filtering-multiselect.scss @@ -49,39 +49,39 @@ vertical-align: top; &.ui-icon-circle-triangle-e { background-position: -16px -32px !important; - background-image: image-url("rails_admin/multiselect/icon_sprite.png"); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/icon_sprite.png"); } &.ui-icon-circle-triangle-e:hover { background-position: -16px -48px !important; - background-image: image-url('rails_admin/multiselect/icon_sprite.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/icon_sprite.png"); } &.ui-icon-circle-triangle-w { background-position: 0px -32px !important; - background-image: image-url('rails_admin/multiselect/icon_sprite.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/icon_sprite.png"); } &.ui-icon-circle-triangle-w:hover { background-position: 0px -48px !important; - background-image: image-url('rails_admin/multiselect/icon_sprite.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/icon_sprite.png"); } &.ui-icon-circle-triangle-n { background-position: 0px 0px !important; margin-left: 1px; - background-image: image-url('rails_admin/multiselect/ui-icon-circle-triangle-n-light.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/ui-icon-circle-triangle-n-light.png"); } &.ui-icon-circle-triangle-n:hover { background-position: 0px 0px !important; margin-left: 1px; - background-image: image-url('rails_admin/multiselect/ui-icon-circle-triangle-n-dark.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/ui-icon-circle-triangle-n-dark.png"); } &.ui-icon-circle-triangle-s { background-position: 0px 0px !important; margin-left: 1px; - background-image: image-url('rails_admin/multiselect/ui-icon-circle-triangle-s-light.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/ui-icon-circle-triangle-s-light.png"); } &.ui-icon-circle-triangle-s:hover { background-position: 0px 0px !important; margin-left: 1px; - background-image: image-url('rails_admin/multiselect/ui-icon-circle-triangle-s-dark.png'); + background-image: url("~rails_admin/src/rails_admin/images/multiselect/ui-icon-circle-triangle-s-dark.png"); } } } diff --git a/app/assets/stylesheets/rails_admin/ra.sidescroll.scss b/src/rails_admin/styles/sidescroll.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/ra.sidescroll.scss rename to src/rails_admin/styles/sidescroll.scss diff --git a/app/assets/stylesheets/rails_admin/themes/cerulean/mixins.scss b/src/rails_admin/styles/themes/cerulean/mixins.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/themes/cerulean/mixins.scss rename to src/rails_admin/styles/themes/cerulean/mixins.scss diff --git a/app/assets/stylesheets/rails_admin/themes/cerulean/theming.scss b/src/rails_admin/styles/themes/cerulean/theming.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/themes/cerulean/theming.scss rename to src/rails_admin/styles/themes/cerulean/theming.scss diff --git a/app/assets/stylesheets/rails_admin/themes/cerulean/variables.scss b/src/rails_admin/styles/themes/cerulean/variables.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/themes/cerulean/variables.scss rename to src/rails_admin/styles/themes/cerulean/variables.scss diff --git a/app/assets/stylesheets/rails_admin/themes/default/mixins.scss b/src/rails_admin/styles/themes/default/mixins.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/themes/default/mixins.scss rename to src/rails_admin/styles/themes/default/mixins.scss diff --git a/app/assets/stylesheets/rails_admin/themes/default/theming.scss b/src/rails_admin/styles/themes/default/theming.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/themes/default/theming.scss rename to src/rails_admin/styles/themes/default/theming.scss diff --git a/app/assets/stylesheets/rails_admin/themes/default/variables.scss b/src/rails_admin/styles/themes/default/variables.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/themes/default/variables.scss rename to src/rails_admin/styles/themes/default/variables.scss diff --git a/app/assets/stylesheets/rails_admin/ra.widgets.scss b/src/rails_admin/styles/widgets.scss similarity index 100% rename from app/assets/stylesheets/rails_admin/ra.widgets.scss rename to src/rails_admin/styles/widgets.scss diff --git a/app/assets/javascripts/rails_admin/ui.js b/src/rails_admin/ui.js similarity index 96% rename from app/assets/javascripts/rails_admin/ui.js rename to src/rails_admin/ui.js index 0bf00e04df..36ea6bb0d1 100644 --- a/app/assets/javascripts/rails_admin/ui.js +++ b/src/rails_admin/ui.js @@ -1,3 +1,7 @@ +import jQuery from 'jquery'; +import 'jquery-ui/ui/effect'; +import I18n from './i18n'; + (function($) { $(document).on("click", "#list input.toggle", function() { $("#list [name='bulk_ids[]']").prop("checked", $(this).is(":checked")); @@ -71,7 +75,7 @@ }); $(document).ready(function() { - RailsAdmin.I18n.init($('html').attr('lang'), $("#admin-js").data('i18nOptions')); + I18n.init($('html').attr('lang'), $("#admin-js").data('i18nOptions')); $(document).trigger('rails_admin.dom_ready'); }); diff --git a/src/rails_admin/vendor/jquery.pjax.js b/src/rails_admin/vendor/jquery.pjax.js new file mode 100644 index 0000000000..c174919bf3 --- /dev/null +++ b/src/rails_admin/vendor/jquery.pjax.js @@ -0,0 +1,899 @@ +/*! + * Copyright 2012, Chris Wanstrath + * Released under the MIT License + * https://github.com/defunkt/jquery-pjax + */ +import jQuery from 'jquery'; + +(function($){ + +// When called on a container with a selector, fetches the href with +// ajax into the container or with the data-pjax attribute on the link +// itself. +// +// Tries to make sure the back button and ctrl+click work the way +// you'd expect. +// +// Exported as $.fn.pjax +// +// Accepts a jQuery ajax options object that may include these +// pjax specific options: +// +// +// container - String selector for the element where to place the response body. +// push - Whether to pushState the URL. Defaults to true (of course). +// replace - Want to use replaceState instead? That's cool. +// +// For convenience the second parameter can be either the container or +// the options object. +// +// Returns the jQuery object +function fnPjax(selector, container, options) { + options = optionsFor(container, options) + return this.on('click.pjax', selector, function(event) { + var opts = options + if (!opts.container) { + opts = $.extend({}, options) + opts.container = $(this).attr('data-pjax') + } + handleClick(event, opts) + }) +} + +// Public: pjax on click handler +// +// Exported as $.pjax.click. +// +// event - "click" jQuery.Event +// options - pjax options +// +// Examples +// +// $(document).on('click', 'a', $.pjax.click) +// // is the same as +// $(document).pjax('a') +// +// Returns nothing. +function handleClick(event, container, options) { + options = optionsFor(container, options) + + var link = event.currentTarget + var $link = $(link) + + if (link.tagName.toUpperCase() !== 'A') + throw "$.fn.pjax or $.pjax.click requires an anchor element" + + // Middle click, cmd click, and ctrl click should open + // links in a new tab as normal. + if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) + return + + // Ignore cross origin links + if ( location.protocol !== link.protocol || location.hostname !== link.hostname ) + return + + // Ignore case when a hash is being tacked on the current URL + if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) ) + return + + // Ignore event with default prevented + if (event.isDefaultPrevented()) + return + + var defaults = { + url: link.href, + container: $link.attr('data-pjax'), + target: link + } + + var opts = $.extend({}, defaults, options) + var clickEvent = $.Event('pjax:click') + $link.trigger(clickEvent, [opts]) + + if (!clickEvent.isDefaultPrevented()) { + pjax(opts) + event.preventDefault() + $link.trigger('pjax:clicked', [opts]) + } +} + +// Public: pjax on form submit handler +// +// Exported as $.pjax.submit +// +// event - "click" jQuery.Event +// options - pjax options +// +// Examples +// +// $(document).on('submit', 'form', function(event) { +// $.pjax.submit(event, '[data-pjax-container]') +// }) +// +// Returns nothing. +function handleSubmit(event, container, options) { + options = optionsFor(container, options) + + var form = event.currentTarget + var $form = $(form) + + if (form.tagName.toUpperCase() !== 'FORM') + throw "$.pjax.submit requires a form element" + + var defaults = { + type: ($form.attr('method') || 'GET').toUpperCase(), + url: $form.attr('action'), + container: $form.attr('data-pjax'), + target: form + } + + if (defaults.type !== 'GET' && window.FormData !== undefined) { + defaults.data = new FormData(form) + defaults.processData = false + defaults.contentType = false + } else { + // Can't handle file uploads, exit + if ($form.find(':file').length) { + return + } + + // Fallback to manually serializing the fields + defaults.data = $form.serializeArray() + } + + pjax($.extend({}, defaults, options)) + + event.preventDefault() +} + +// Loads a URL with ajax, puts the response body inside a container, +// then pushState()'s the loaded URL. +// +// Works just like $.ajax in that it accepts a jQuery ajax +// settings object (with keys like url, type, data, etc). +// +// Accepts these extra keys: +// +// container - String selector for where to stick the response body. +// push - Whether to pushState the URL. Defaults to true (of course). +// replace - Want to use replaceState instead? That's cool. +// +// Use it just like $.ajax: +// +// var xhr = $.pjax({ url: this.href, container: '#main' }) +// console.log( xhr.readyState ) +// +// Returns whatever $.ajax returns. +function pjax(options) { + options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options) + + if ($.isFunction(options.url)) { + options.url = options.url() + } + + var hash = parseURL(options.url).hash + + var containerType = $.type(options.container) + if (containerType !== 'string') { + throw "expected string value for 'container' option; got " + containerType + } + var context = options.context = $(options.container) + if (!context.length) { + throw "the container selector '" + options.container + "' did not match anything" + } + + // We want the browser to maintain two separate internal caches: one + // for pjax'd partial page loads and one for normal page loads. + // Without adding this secret parameter, some browsers will often + // confuse the two. + if (!options.data) options.data = {} + if ($.isArray(options.data)) { + options.data.push({name: '_pjax', value: options.container}) + } else { + options.data._pjax = options.container + } + + function fire(type, args, props) { + if (!props) props = {} + props.relatedTarget = options.target + var event = $.Event(type, props) + context.trigger(event, args) + return !event.isDefaultPrevented() + } + + var timeoutTimer + + options.beforeSend = function(xhr, settings) { + // No timeout for non-GET requests + // Its not safe to request the resource again with a fallback method. + if (settings.type !== 'GET') { + settings.timeout = 0 + } + + xhr.setRequestHeader('X-PJAX', 'true') + xhr.setRequestHeader('X-PJAX-Container', options.container) + + if (!fire('pjax:beforeSend', [xhr, settings])) + return false + + if (settings.timeout > 0) { + timeoutTimer = setTimeout(function() { + if (fire('pjax:timeout', [xhr, options])) + xhr.abort('timeout') + }, settings.timeout) + + // Clear timeout setting so jquerys internal timeout isn't invoked + settings.timeout = 0 + } + + var url = parseURL(settings.url) + if (hash) url.hash = hash + options.requestUrl = stripInternalParams(url) + } + + options.complete = function(xhr, textStatus) { + if (timeoutTimer) + clearTimeout(timeoutTimer) + + fire('pjax:complete', [xhr, textStatus, options]) + + fire('pjax:end', [xhr, options]) + } + + options.error = function(xhr, textStatus, errorThrown) { + var container = extractContainer("", xhr, options) + + var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]) + if (options.type == 'GET' && textStatus !== 'abort' && allowed) { + locationReplace(container.url) + } + } + + options.success = function(data, status, xhr) { + var previousState = pjax.state + + // If $.pjax.defaults.version is a function, invoke it first. + // Otherwise it can be a static string. + var currentVersion = (typeof $.pjax.defaults.version === 'function') ? + $.pjax.defaults.version() : + $.pjax.defaults.version + + var latestVersion = xhr.getResponseHeader('X-PJAX-Version') + + var container = extractContainer(data, xhr, options) + + var url = parseURL(container.url) + if (hash) { + url.hash = hash + container.url = url.href + } + + // If there is a layout version mismatch, hard load the new url + if (currentVersion && latestVersion && currentVersion !== latestVersion) { + locationReplace(container.url) + return + } + + // If the new response is missing a body, hard load the page + if (!container.contents) { + locationReplace(container.url) + return + } + + pjax.state = { + id: options.id || uniqueId(), + url: container.url, + title: container.title, + container: options.container, + fragment: options.fragment, + timeout: options.timeout + } + + if (options.push || options.replace) { + window.history.replaceState(pjax.state, container.title, container.url) + } + + // Only blur the focus if the focused element is within the container. + var blurFocus = $.contains(options.container, document.activeElement) + + // Clear out any focused controls before inserting new page contents. + if (blurFocus) { + try { + document.activeElement.blur() + } catch (e) { } + } + + if (container.title) document.title = container.title + + fire('pjax:beforeReplace', [container.contents, options], { + state: pjax.state, + previousState: previousState + }) + context.html(container.contents) + + // FF bug: Won't autofocus fields that are inserted via JS. + // This behavior is incorrect. So if theres no current focus, autofocus + // the last field. + // + // http://www.w3.org/html/wg/drafts/html/master/forms.html + var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0] + if (autofocusEl && document.activeElement !== autofocusEl) { + autofocusEl.focus() + } + + executeScriptTags(container.scripts) + + var scrollTo = options.scrollTo + + // Ensure browser scrolls to the element referenced by the URL anchor + if (hash) { + var name = decodeURIComponent(hash.slice(1)) + var target = document.getElementById(name) || document.getElementsByName(name)[0] + if (target) scrollTo = $(target).offset().top + } + + if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo) + + fire('pjax:success', [data, status, xhr, options]) + } + + + // Initialize pjax.state for the initial page load. Assume we're + // using the container and options of the link we're loading for the + // back button to the initial page. This ensures good back button + // behavior. + if (!pjax.state) { + pjax.state = { + id: uniqueId(), + url: window.location.href, + title: document.title, + container: options.container, + fragment: options.fragment, + timeout: options.timeout + } + window.history.replaceState(pjax.state, document.title) + } + + // Cancel the current request if we're already pjaxing + abortXHR(pjax.xhr) + + pjax.options = options + var xhr = pjax.xhr = $.ajax(options) + + if (xhr.readyState > 0) { + if (options.push && !options.replace) { + // Cache current container element before replacing it + cachePush(pjax.state.id, [options.container, cloneContents(context)]) + + window.history.pushState(null, "", options.requestUrl) + } + + fire('pjax:start', [xhr, options]) + fire('pjax:send', [xhr, options]) + } + + return pjax.xhr +} + +// Public: Reload current page with pjax. +// +// Returns whatever $.pjax returns. +function pjaxReload(container, options) { + var defaults = { + url: window.location.href, + push: false, + replace: true, + scrollTo: false + } + + return pjax($.extend(defaults, optionsFor(container, options))) +} + +// Internal: Hard replace current state with url. +// +// Work for around WebKit +// https://bugs.webkit.org/show_bug.cgi?id=93506 +// +// Returns nothing. +function locationReplace(url) { + window.history.replaceState(null, "", pjax.state.url) + window.location.replace(url) +} + + +var initialPop = true +var initialURL = window.location.href +var initialState = window.history.state + +// Initialize $.pjax.state if possible +// Happens when reloading a page and coming forward from a different +// session history. +if (initialState && initialState.container) { + pjax.state = initialState +} + +// Non-webkit browsers don't fire an initial popstate event +if ('state' in window.history) { + initialPop = false +} + +// popstate handler takes care of the back and forward buttons +// +// You probably shouldn't use pjax on pages with other pushState +// stuff yet. +function onPjaxPopstate(event) { + + // Hitting back or forward should override any pending PJAX request. + if (!initialPop) { + abortXHR(pjax.xhr) + } + + var previousState = pjax.state + var state = event.state + var direction + + if (state && state.container) { + // When coming forward from a separate history session, will get an + // initial pop with a state we are already at. Skip reloading the current + // page. + if (initialPop && initialURL == state.url) return + + if (previousState) { + // If popping back to the same state, just skip. + // Could be clicking back from hashchange rather than a pushState. + if (previousState.id === state.id) return + + // Since state IDs always increase, we can deduce the navigation direction + direction = previousState.id < state.id ? 'forward' : 'back' + } + + var cache = cacheMapping[state.id] || [] + var containerSelector = cache[0] || state.container + var container = $(containerSelector), contents = cache[1] + + if (container.length) { + if (previousState) { + // Cache current container before replacement and inform the + // cache which direction the history shifted. + cachePop(direction, previousState.id, [containerSelector, cloneContents(container)]) + } + + var popstateEvent = $.Event('pjax:popstate', { + state: state, + direction: direction + }) + container.trigger(popstateEvent) + + var options = { + id: state.id, + url: state.url, + container: containerSelector, + push: false, + fragment: state.fragment, + timeout: state.timeout, + scrollTo: false + } + + if (contents) { + container.trigger('pjax:start', [null, options]) + + pjax.state = state + if (state.title) document.title = state.title + var beforeReplaceEvent = $.Event('pjax:beforeReplace', { + state: state, + previousState: previousState + }) + container.trigger(beforeReplaceEvent, [contents, options]) + container.html(contents) + + container.trigger('pjax:end', [null, options]) + } else { + pjax(options) + } + + // Force reflow/relayout before the browser tries to restore the + // scroll position. + container[0].offsetHeight + } else { + locationReplace(location.href) + } + } + initialPop = false +} + +// Fallback version of main pjax function for browsers that don't +// support pushState. +// +// Returns nothing since it retriggers a hard form submission. +function fallbackPjax(options) { + var url = $.isFunction(options.url) ? options.url() : options.url, + method = options.type ? options.type.toUpperCase() : 'GET' + + var form = $('