From f1c670a2b09a292a0108186cc132420f9a709ff4 Mon Sep 17 00:00:00 2001 From: Alessandro Barbieri Date: Tue, 6 Feb 2018 16:46:09 +0100 Subject: [PATCH 01/38] Remove preview from structure sidebar if is new record --- README.md | 2 +- .../binda/structures/_form_sidebar.html.erb | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ce246d05..55fdae08 100644 --- a/README.md +++ b/README.md @@ -953,7 +953,7 @@ More info can be found at the [semantic versioning documentation](https://semver ### Bug reporting Please refer to this [guide](http://yourbugreportneedsmore.info). -If you need direct help you can join [Binda Slack Community](https://bindacms.slack.com). +If you need direct help you can join [Binda Slack Channel](https://bindacms.slack.com). ### License diff --git a/app/views/binda/structures/_form_sidebar.html.erb b/app/views/binda/structures/_form_sidebar.html.erb index 4c554da1..15f8f3ca 100644 --- a/app/views/binda/structures/_form_sidebar.html.erb +++ b/app/views/binda/structures/_form_sidebar.html.erb @@ -10,14 +10,15 @@ include_blank: false, label_method: -> (x){ "#{x}".html_safe }%> -
- <%= f.input :has_preview, - label: "#{ t('binda.has_preview')}".html_safe, - as: :boolean, - include_blank: false, - hint: "#{t('binda.preview_path_warning')}:
#{binda.root_path}#{@structure.slug}/:slug".html_safe %> -
- +<% if !@structure.new_record? %> +
+ <%= f.input :has_preview, + label: "#{ t('binda.has_preview')}".html_safe, + as: :boolean, + include_blank: false, + hint: "#{t('binda.preview_path_warning')}:
#{binda.root_path}#{@structure.slug}/:slug".html_safe %> +
+<% end %> <% if !@structure.new_record? && @structure.instance_type == 'component' %>
<%= f.input :has_categories, From 8dd4bb355c9620c2f0d2402a4167346ef0c256f7 Mon Sep 17 00:00:00 2001 From: Alessandro Barbieri Date: Tue, 6 Feb 2018 17:31:15 +0100 Subject: [PATCH 02/38] Better organize code on repeater views --- .../binda/fieldable/_form_item_date.html.erb | 2 +- .../binda/fieldable/_form_item_image.html.erb | 2 +- .../fieldable/_form_item_relation.html.erb | 6 +- .../fieldable/_form_item_repeater.html.erb | 108 ++---------------- .../fieldable/_form_item_selections.html.erb | 2 +- .../fieldable/_form_item_string.html.erb | 4 +- .../binda/fieldable/_form_item_text.html.erb | 4 +- .../binda/fieldable/_form_item_video.html.erb | 2 +- .../_form_item_repeater_empty_list.html.erb | 38 ++++++ .../_form_item_repeater_header.html.erb | 25 ++++ .../_form_item_repeater_list.html.erb | 16 +++ ...form_item_repeater_populated_list.html.erb | 40 +++++++ 12 files changed, 139 insertions(+), 110 deletions(-) create mode 100644 app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb create mode 100644 app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb create mode 100644 app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb create mode 100644 app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb diff --git a/app/views/binda/fieldable/_form_item_date.html.erb b/app/views/binda/fieldable/_form_item_date.html.erb index f42dc240..e7b6da05 100644 --- a/app/views/binda/fieldable/_form_item_date.html.erb +++ b/app/views/binda/fieldable/_form_item_date.html.erb @@ -4,7 +4,7 @@ start_year: Date.today.year - 200, end_year: Date.today.year + 200, hint: field_setting.description, - label: field_setting.name.capitalize, + label: field_setting.name, input_html: { class: 'select2-item' } %> <%= ff.input :field_setting_id, as: :hidden, input_html: { value: field_setting.id } %> <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> diff --git a/app/views/binda/fieldable/_form_item_image.html.erb b/app/views/binda/fieldable/_form_item_image.html.erb index c8990380..ca4e7510 100644 --- a/app/views/binda/fieldable/_form_item_image.html.erb +++ b/app/views/binda/fieldable/_form_item_image.html.erb @@ -4,7 +4,7 @@ data-message="<%= t('binda.upload_loading_message') %>" data-error="<%= t('binda.upload_error_message')%>">

- <%= field_setting.name.capitalize %> + <%= field_setting.name %>

<% image = ff.object.image.thumb.url if ff.object.image.present? %>
diff --git a/app/views/binda/fieldable/_form_item_relation.html.erb b/app/views/binda/fieldable/_form_item_relation.html.erb index c18fdcd9..5d06f701 100644 --- a/app/views/binda/fieldable/_form_item_relation.html.erb +++ b/app/views/binda/fieldable/_form_item_relation.html.erb @@ -6,7 +6,7 @@ <%= ff.input :dependent_component_ids, collection: get_relationable_components(field_setting), as: :select, input_html: { multiple: true, class: 'select2-item' }, - label: field_setting.name.capitalize, + label: field_setting.name, hint: field_setting.description, value_method: :id, checked: ff.object.dependent_component_ids, @@ -19,7 +19,7 @@ <%= ff.input :dependent_board_ids, collection: get_relationable_boards(field_setting), as: :select, input_html: { multiple: true, class: 'select2-item' }, - label: field_setting.name.capitalize, + label: field_setting.name, hint: field_setting.description, value_method: :id, checked: ff.object.dependent_board_ids, @@ -31,7 +31,7 @@ <% unless field_setting.accepted_structures.any? %>
- +

<%= t('binda.relation_warning') %>

diff --git a/app/views/binda/fieldable/_form_item_repeater.html.erb b/app/views/binda/fieldable/_form_item_repeater.html.erb index a89d44ee..49f131a9 100644 --- a/app/views/binda/fieldable/_form_item_repeater.html.erb +++ b/app/views/binda/fieldable/_form_item_repeater.html.erb @@ -1,102 +1,12 @@ -
-
- -
- - <%# SORT REPEATER FIELDS %> - - - <%= t'binda.done' %> - - - <%= t'binda.sort_items' %> - - - - <%# ADD NEW REPEATER FIELDS %> - - <%= t('binda.new_item_in_repeater', arg1: repeater_setting.name.capitalize ) %> - - - <%# REPEATER NAME %> -

<%= repeater_setting.name.capitalize %>

- -
- -
    - - <% if repeaters.size == 0 %> - - <% repeater = @instance.find_or_create_a_field_by( repeater_setting.id, 'repeater' ) %> - -
  • - - <%# - - - - - - - - - - - - %> - <%# REPEATER HEADER %> - <%# - - - - - - - - - - - - %> -
    - - <%= t('binda.collapse') %> - <%= t('binda.expand') %> - - <%= t('binda.delete').capitalize %> -

    <%= repeater.field_setting.name %> <%= t('binda.item') %> #<%= repeater.id %>

    -
    - - <%# - - - - - - - - - - - - - - %> - <%# FIRST REPEATER %> - <%# - - - - - - - - - - - - - - %> -
    - <%= f.simple_fields_for "repeaters_attributes[]", repeater do |ff| %> - <%= ff.input :fieldable_id, as: :hidden %> - <%= ff.input :fieldable_type, as: :hidden %> - <%= ff.input :field_setting_id, as: :hidden, input_html: { value: repeater_setting.id } %> - <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> - <% repeater_setting.children.order( :position, :id ).each do |repeater_setting_child| %> - <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child %> - <% end %> - <% end %> -
    -
  • - - <% else %> - - <% repeaters.each do |repeater| %> -
  • - - <%# - - - - - - - - - - - - %> - <%# REPEATER HEADER %> - <%# - - - - - - - - - - - - %> -
    - - <%= t('binda.collapse') %> - <%= t('binda.expand') %> - - <%= t('binda.delete').capitalize %> -

    <%= repeater.field_setting.name %> <%= t('binda.item') %> #<%= repeater.id %>

    -
    - <%# - - - - - - - - - - - - - - %> - <%# EACH REPEATER %> - <%# - - - - - - - - - - - - - - %> -
    - <%= f.simple_fields_for "repeaters_attributes[]", repeater do |ff| %> - <%= ff.input :field_setting_id, as: :hidden, input_html: { value: ff.object.field_setting.id } %> - <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> - <%= ff.input :fieldable_id, as: :hidden %> - <%= ff.input :fieldable_type, as: :hidden %> - <% ff.object.field_setting.children.order( :position, :id ).each do |repeater_setting_child| %> - <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child, repeater: repeater %> - <% end %> - <% end %> -
    - -
  • - <% end %> - - <% end %> -
- +
+
+ <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_header', + repeater_setting: repeater_setting %> + <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_list', + repeater_setting: repeater_setting, + repeaters: repeaters, + f: f %>
-
\ No newline at end of file diff --git a/app/views/binda/fieldable/_form_item_selections.html.erb b/app/views/binda/fieldable/_form_item_selections.html.erb index a0e14011..a542882e 100644 --- a/app/views/binda/fieldable/_form_item_selections.html.erb +++ b/app/views/binda/fieldable/_form_item_selections.html.erb @@ -10,7 +10,7 @@ <% else %>

- <%= field_setting.name.capitalize %> + <%= field_setting.name %>

<%= t('binda.no_available_choice') %> diff --git a/app/views/binda/fieldable/_form_item_string.html.erb b/app/views/binda/fieldable/_form_item_string.html.erb index e55a3601..19db6259 100644 --- a/app/views/binda/fieldable/_form_item_string.html.erb +++ b/app/views/binda/fieldable/_form_item_string.html.erb @@ -5,12 +5,12 @@ input_html: { value: @instance.get_string( field_setting.slug ) }, as: :string, hint: field_setting.description, - label: field_setting.name.capitalize %> + label: field_setting.name %> <% else %> <%= ff.input :content, as: :string, hint: field_setting.description, - label: field_setting.name.capitalize %> + label: field_setting.name %> <% end %> <%= ff.input :field_setting_id, as: :hidden, input_html: { value: field_setting.id } %> diff --git a/app/views/binda/fieldable/_form_item_text.html.erb b/app/views/binda/fieldable/_form_item_text.html.erb index ec523af3..896c05d9 100644 --- a/app/views/binda/fieldable/_form_item_text.html.erb +++ b/app/views/binda/fieldable/_form_item_text.html.erb @@ -5,13 +5,13 @@ input_html: { value: @instance.get_text( field_setting.slug ), class: 'tinymce' }, as: :text, hint: field_setting.description, - label: field_setting.name.capitalize %> + label: field_setting.name %> <% else %> <%= ff.input :content, input_html: { class: 'tinymce' }, as: :text, hint: field_setting.description, - label: field_setting.name.capitalize %> + label: field_setting.name %> <% end %> <%= ff.input :field_setting_id, as: :hidden, input_html: { value: field_setting.id } %> diff --git a/app/views/binda/fieldable/_form_item_video.html.erb b/app/views/binda/fieldable/_form_item_video.html.erb index 6d97fcf1..cea5c8fc 100644 --- a/app/views/binda/fieldable/_form_item_video.html.erb +++ b/app/views/binda/fieldable/_form_item_video.html.erb @@ -4,7 +4,7 @@ data-message="<%= t('binda.upload_loading_message') %>" data-error="<%= t('binda.upload_error_message')%>"> -

<%= field_setting.name.capitalize %>

+

<%= field_setting.name %>

diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb new file mode 100644 index 00000000..ceddbb8d --- /dev/null +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb @@ -0,0 +1,38 @@ +<% repeater = @instance.find_or_create_a_field_by( repeater_setting.id, 'repeater' ) %> +
  • +
    + + + + <%= t('binda.collapse') %> + + + + <%= t('binda.expand') %> + + + + + <%= t('binda.delete').capitalize %> + +

    + <%= repeater.field_setting.name %> + <%= t('binda.item') %> + #<%= repeater.id %> +

    +
    +
    + <%= f.simple_fields_for "repeaters_attributes[]", repeater do |ff| %> + <%= ff.input :fieldable_id, as: :hidden %> + <%= ff.input :fieldable_type, as: :hidden %> + <%= ff.input :field_setting_id, as: :hidden, input_html: { value: repeater_setting.id } %> + <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> + <% repeater_setting.children.order( :position, :id ).each do |repeater_setting_child| %> + <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child %> + <% end %> + <% end %> +
    +
  • \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb new file mode 100644 index 00000000..0b4139ff --- /dev/null +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb new file mode 100644 index 00000000..28006e3b --- /dev/null +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb @@ -0,0 +1,16 @@ +
      + <% if repeaters.size == 0 %> + <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_empty_list', + repeaters: repeaters, + repeater_setting: repeater_setting, + f: f %> + <% else %> + <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_default_list', + repeaters: repeaters, + repeater_setting: repeater_setting, + f: f %> + <% end %> +
    \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb new file mode 100644 index 00000000..a09fdf06 --- /dev/null +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb @@ -0,0 +1,40 @@ +<% repeaters.each do |repeater| %> +
  • +
    + + + + <%= t('binda.collapse') %> + + + + <%= t('binda.expand') %> + + + + + <%= t('binda.delete').capitalize %> + +

    + <%= repeater.field_setting.name %> + <%= t('binda.item') %> + #<%= repeater.id %> +

    +
    +
    + <%= f.simple_fields_for "repeaters_attributes[]", repeater do |ff| %> + <%= ff.input :field_setting_id, as: :hidden, input_html: { value: ff.object.field_setting.id } %> + <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> + <%= ff.input :fieldable_id, as: :hidden %> + <%= ff.input :fieldable_type, as: :hidden %> + <% ff.object.field_setting.children.order( :position, :id ).each do |repeater_setting_child| %> + <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child, repeater: repeater %> + <% end %> + <% end %> +
    +
  • +<% end %> \ No newline at end of file From d5d1d90b226f101984215966b6ece239acd12507 Mon Sep 17 00:00:00 2001 From: Alessandro Barbieri Date: Tue, 6 Feb 2018 17:51:30 +0100 Subject: [PATCH 03/38] Add target blank to preview anchor tag --- app/views/binda/boards/edit.html.erb | 3 ++- app/views/binda/components/edit.html.erb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/binda/boards/edit.html.erb b/app/views/binda/boards/edit.html.erb index c9c46172..9dba9126 100644 --- a/app/views/binda/boards/edit.html.erb +++ b/app/views/binda/boards/edit.html.erb @@ -7,7 +7,8 @@
    <% if @structure.has_preview %> " - class="main-header--link b-btn b-btn-primary"> + class="main-header--link b-btn b-btn-primary" + target="_blank"> <%= t 'binda.preview' %> <%= @structure.name.humanize.split.map(&:capitalize).join(' ') %> diff --git a/app/views/binda/components/edit.html.erb b/app/views/binda/components/edit.html.erb index 67320faf..8ef372f6 100644 --- a/app/views/binda/components/edit.html.erb +++ b/app/views/binda/components/edit.html.erb @@ -8,7 +8,8 @@
    <% if @structure.has_preview %> " - class="main-header--link b-btn b-btn-primary"> + class="main-header--link b-btn b-btn-primary" + target="_blank"> <%= t 'binda.preview' %> <%= @structure.name.humanize.split.map(&:capitalize).join(' ') %> From 3d3e85ed952824be2af29cce5daba57cbae637c3 Mon Sep 17 00:00:00 2001 From: Alessandro Barbieri Date: Fri, 9 Feb 2018 10:38:02 +0100 Subject: [PATCH 04/38] FIX install not working anymore, add tests as well --- Gemfile | 29 ++++++- Gemfile.lock | 4 + Rakefile | 4 + app/models/binda/user.rb | 10 +++ binda.gemspec | 1 + .../binda/install/install_generator.rb | 22 +++-- lib/generators/binda/setup/setup_generator.rb | 30 +++---- lib/tasks/user_tasks.rake | 8 +- .../binda/install_generator_spec.rb | 63 ++++++++++++++ .../binda/install_generator_test.rb | 17 ---- .../generators/binda/setup_generator_spec.rb | 87 +++++++++++++++++++ 11 files changed, 228 insertions(+), 47 deletions(-) create mode 100644 spec/lib/generators/binda/install_generator_spec.rb delete mode 100644 spec/lib/generators/binda/install_generator_test.rb create mode 100644 spec/lib/generators/binda/setup_generator_spec.rb diff --git a/Gemfile b/Gemfile index 4b1c9e33..4f260ba9 100644 --- a/Gemfile +++ b/Gemfile @@ -13,4 +13,31 @@ gemspec # To use a debugger # gem 'byebug', group: [:development, :test] -gem 'simplecov', :require => false, :group => :test \ No newline at end of file +# group :development do +# gem "listen", "~> 3.1" +# gem "autoprefixer-rails", "~> 7.1", "< 8" +# end + +# group :development, :test do +# gem "pg", ">= 0.21", "< 1.0" +# gem "pry-rails", "~> 0.3.5" +# gem "bullet", ">= 5.6", "< 6" +# gem "rspec-rails", ">= 3.5", "< 3.8" +# gem "generator_spec", "~> 0.9.4" +# end + gem 'simplecov' + +# group :test do +# gem 'simplecov' +# gem "capybara", ">= 2.14", "< 3" +# gem "selenium-webdriver", "~> 3.5", "< 4" +# gem "factory_bot_rails", "~> 4.8" +# gem "database_cleaner", ">= 1.6", "< 2" +# gem "yard", "> 0.9.11", "< 1.0" +# gem "yard-activesupport-concern", "~> 0.0.1", "< 0.1" +# gem "redcarpet", "~> 3.4" +# gem "github-markup", ">= 1.6", "< 2" +# gem "travis", "~> 1.8" +# gem "rubocop", "~> 0.52.1" +# gem "mry", "~> 0.52" +# end \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 48edd6af..a901a6c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,6 +120,9 @@ GEM ffi (1.9.18) friendly_id (5.2.3) activerecord (>= 4.0.0) + generator_spec (0.9.4) + activesupport (>= 3.0.0) + railties (>= 3.0.0) gh (0.15.1) addressable (~> 2.4.0) backports @@ -329,6 +332,7 @@ DEPENDENCIES capybara (>= 2.14, < 3) database_cleaner (>= 1.6, < 2) factory_bot_rails (~> 4.8) + generator_spec (~> 0.9.4) github-markup (>= 1.6, < 2) listen (~> 3.1) mry (~> 0.52) diff --git a/Rakefile b/Rakefile index 130d38ce..e76b41e7 100644 --- a/Rakefile +++ b/Rakefile @@ -14,6 +14,10 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end +# https://www.viget.com/articles/rails-engine-testing-with-rspec-capybara-and-factorygirl/ +# Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f } +# require 'rspec/core' + APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__) load 'rails/tasks/engine.rake' diff --git a/app/models/binda/user.rb b/app/models/binda/user.rb index 65bb6da9..c17f4332 100644 --- a/app/models/binda/user.rb +++ b/app/models/binda/user.rb @@ -4,5 +4,15 @@ class User < ApplicationRecord # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable + + def self.create_super_admin_user + STDOUT.puts "What is your email? [mail@domain.com]" + username = STDIN.gets.strip + username = 'mail@domain.com' if username.blank? + STDOUT.puts "What is your password? [password]" + password = STDIN.gets.strip + password = 'password' if password.blank? + User.create!( email: username, password: password, password_confirmation: password, is_superadmin: true ) + end end end diff --git a/binda.gemspec b/binda.gemspec index f0a407a1..fcf19ea0 100644 --- a/binda.gemspec +++ b/binda.gemspec @@ -58,5 +58,6 @@ Gem::Specification.new do |s| s.add_development_dependency "travis", "~> 1.8" s.add_development_dependency "rubocop", "~> 0.52.1" s.add_development_dependency "mry", "~> 0.52" + s.add_development_dependency "generator_spec", "~> 0.9.4" end diff --git a/lib/generators/binda/install/install_generator.rb b/lib/generators/binda/install/install_generator.rb index d36ce0a1..3109a2c6 100644 --- a/lib/generators/binda/install/install_generator.rb +++ b/lib/generators/binda/install/install_generator.rb @@ -6,7 +6,7 @@ class InstallGenerator < Rails::Generators::Base def check_if_production if Rails.env.production? - puts "Sorry Binda can only be installed in development mode" + warn "Sorry Binda can only be installed in development mode" exit end end @@ -14,8 +14,8 @@ def check_if_production def check_previous_install # Ensure Binda is not installed if ActiveRecord::Base.connection.data_source_exists? 'binda_components' - puts "Binda has already been installed on this database." - puts "Please ensure Binda is completely removed from the database before trying to install it again." + warn "Binda has already been installed on this database." + warn "Please ensure Binda is completely removed from the database before trying to install it again." exit end end @@ -37,8 +37,8 @@ def run_migrations else # If there is any previous Binda migration if previous_migrations.size != previous_binda_migrations.size - puts "You have several migrations, please manually delete Binda's ones then run 'rails g binda:install' again." - puts "Keep in mind that Binda will place the new migration after the existing ones." + warn "You have several migrations, please manually delete Binda's ones then run 'rails g binda:install' again." + warn "Keep in mind that Binda will place the new migration after the existing ones." exit else # Remove previous Binda migrations @@ -119,6 +119,18 @@ def setup_carrierwave template 'config/initializers/carrierwave.rb' puts "===============================================================================" end + + # Setup default helpers + # + # This operation creates a class called `B` from which is possible to call any + # Binda helper contained in Binda::DefaultHelpers. This is possible by inheriting the + # `Binda::B` class. + def setup_default_helpers + puts "5) Setting up default helpers" + generate "model", "B --no-migration --parent=::Binda::B" + puts "Default helpers has been set up." + puts + end def setup_settings exec 'rails generate binda:maintenance && rails generate binda:setup' diff --git a/lib/generators/binda/setup/setup_generator.rb b/lib/generators/binda/setup/setup_generator.rb index 865e775a..effc47fa 100644 --- a/lib/generators/binda/setup/setup_generator.rb +++ b/lib/generators/binda/setup/setup_generator.rb @@ -24,7 +24,7 @@ def setup_settings def create_credentials puts "1) Create a superadmin user" - rake 'binda:create_superadmin_user' + User.create_super_admin_user puts end @@ -33,7 +33,7 @@ def setup_maintenance_mode # Use radio field_type untill truefalse isn't available unless FieldSetting.find_by(slug: 'maintenance-mode').present? - maintenance_mode = @field_settings.create!( name: 'Maintenance Mode', field_type: 'radio', position: 1, allow_null: false ) + maintenance_mode = @field_settings.create!( name: 'Maintenance Mode', field_type: 'radio', position: 1, allow_null: false, slug: 'maintenance-mode' ) # create active and disabled choices disabled = maintenance_mode.choices.create!( label: 'disabled', value: 'false' ) maintenance_mode.choices.create!( label: 'active', value: 'true' ) @@ -41,7 +41,8 @@ def setup_maintenance_mode # assign disabled choice and remove the temporary choice @dashboard.reload @dashboard.radios.first.choices << disabled - @dashboard.radios.first.choices.select{|choice| choice.label != 'disabled'}.first.destroy + unwanted = @dashboard.radios.first.choices.select{|choice| choice.label != 'disabled'} + unwanted.each{|choice| choice.destroy} if unwanted.any? end puts "The maintenance-mode option has been set up." puts @@ -57,7 +58,10 @@ def setup_website_name # make sure slug works website_name_obj.update_attribute( 'slug', 'website-name' ) end - website_name = ask("How would you like to name your website? ['MySite']\n").presence || 'MySite' + # website_name = ask("How would you like to name your website? ['MySite']\n").presence || 'MySite' + STDOUT.puts "How would you like to name your website? ['MySite']" + website_name = STDIN.gets + website_name = 'MySite' if website_name.blank? @dashboard.strings.find_or_create_by( field_setting_id: website_name_obj.id ).update_attribute('content', website_name ) end @@ -71,22 +75,14 @@ def setup_website_content # make sure slug works website_description_obj.update_attribute( 'slug', 'website-description' ) end - website_description = ask("What is your website about? ['A website about the world']\n").presence || 'A website about the world' + # website_description = ask("What is your website about? ['A website about the world']\n").presence || 'A website about the world' + + STDOUT.puts "What is your website about? ['A website about the world']" + website_description = STDIN.gets + website_description = 'A website about the world' if website_description.blank? @dashboard.texts.find_or_create_by!( field_setting_id: website_description_obj.id ).update_attribute( 'content', website_description ) end - # Setup default helpers - # - # This operation creates a class called `B` from which is possible to call any - # Binda helper contained in Binda::DefaultHelpers. This is possible by inheriting the - # `Binda::B` class. - def setup_default_helpers - puts "5) Setting up default helpers" - generate "model", "B --no-migration --parent=::Binda::B" - puts "Default helpers has been set up." - puts - end - def feedback puts "===============================================================================" puts diff --git a/lib/tasks/user_tasks.rake b/lib/tasks/user_tasks.rake index c491c48e..36da59a1 100644 --- a/lib/tasks/user_tasks.rake +++ b/lib/tasks/user_tasks.rake @@ -2,13 +2,7 @@ namespace :binda do desc "Create super admin user" task :create_superadmin_user => :environment do - STDOUT.puts "What is your email? [mail@domain.com]" - username = STDIN.gets.strip - username = 'mail@domain.com' if username.blank? - STDOUT.puts "What is your password? [password]" - password = STDIN.gets.strip - password = 'password' if password.blank? - Binda::User.create!( email: username, password: password, password_confirmation: password, is_superadmin: true ) + Binda::User.create_super_admin_user end end \ No newline at end of file diff --git a/spec/lib/generators/binda/install_generator_spec.rb b/spec/lib/generators/binda/install_generator_spec.rb new file mode 100644 index 00000000..40cd1b8a --- /dev/null +++ b/spec/lib/generators/binda/install_generator_spec.rb @@ -0,0 +1,63 @@ +require "generator_spec" +require "rails_helper" +require "./lib/generators/binda/install/install_generator.rb" + +require 'rake' + +module Binda + describe InstallGenerator, type: :generator do + destination File.expand_path("../../tmp", __FILE__) + + after(:context) do + system "rails db:migrate RAILS_ENV=test" + ::DatabaseCleaner.strategy = :truncation + ::DatabaseCleaner.clean + ::Rails.application.load_seed + ::FactoryBot.create(:user) + end + + describe "executing from a brand new application" do + + before(:context) do + + # TODO setup properly + + # assuming there is one only migration + # system "rails db:rollback" + # # https://stackoverflow.com/a/15350752/1498118 + # # https://apidock.com/rails/Rails/Engine/load_tasks + # # ::Rails::Engine.config.load_tasks + # # wait at least 10 sec so we can be sure migration are setup properly + # # REVIEW it'd be best to use a callback instead of waiting X amount of time + # sleep 10 + # prepare_destination + # run_generator + end + + it "checks if production" do + skip("To be implemented") + end + it "exits if previous install is present" do + skip("To be implemented") + end + it "runs migrations" do + skip("To be implemented") + end + it "adds routes" do + skip("To be implemented") + end + it "setups devise" do + skip("To be implemented") + end + it "setups carrierwave" do + skip("To be implemented") + end + it "setups defalt helpers" do + skip("To be implemented") + end + it "launches binda setup and maintenance mode setup" do + skip("To be implemented") + end + end + end +end \ No newline at end of file diff --git a/spec/lib/generators/binda/install_generator_test.rb b/spec/lib/generators/binda/install_generator_test.rb deleted file mode 100644 index 0598723d..00000000 --- a/spec/lib/generators/binda/install_generator_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'test_helper' -require 'generators/binda/install/install_generator' - -module Binda - class InstallGeneratorTest < Rails::Generators::TestCase - tests InstallGenerator - destination Rails.root.join('tmp/generators') - setup :prepare_destination - - - # test "generator runs without errors" do - # assert_nothing_raised do - # run_generator ["arguments"] - # end - # end - end -end diff --git a/spec/lib/generators/binda/setup_generator_spec.rb b/spec/lib/generators/binda/setup_generator_spec.rb new file mode 100644 index 00000000..c82b89af --- /dev/null +++ b/spec/lib/generators/binda/setup_generator_spec.rb @@ -0,0 +1,87 @@ +require "generator_spec" +require "rails_helper" +require "./lib/generators/binda/setup/setup_generator.rb" + +module Binda + describe SetupGenerator, type: :generator do + destination File.expand_path("../../tmp", __FILE__) + + after(:all) do + ::DatabaseCleaner.strategy = :truncation + ::DatabaseCleaner.clean + # http://stackoverflow.com/a/19930700/1498118 + ::Rails.application.load_seed # loading seeds + # Create first user + ::FactoryBot.create(:user) + end + + describe "executing from a brand new application" do + + before(:all) do + ::DatabaseCleaner.strategy = :truncation + ::DatabaseCleaner.clean + + prepare_destination + # @old_stdin = STDIN + STDIN = StringIO.new("\n\n\n\n") + run_generator + # STDIN = @old_stdin + end + + it "creates one maintenance mode field" do + maintenance_mode_array = Binda::Radio + .where( + field_setting_id: Binda::FieldSetting.where( + slug: 'maintenance-mode' + ).ids + ) + expect(maintenance_mode_array.length).to eq 1 + end + + it "assigns one choice to the maintenance mode field" do + maintenance_mode = Binda::Radio + .where( + field_setting_id: Binda::FieldSetting.where( + slug: 'maintenance-mode' + ).ids + ).first + expect(maintenance_mode.choices.length).to eq 1 + end + + it "set maintenance mode as disabled" do + maintenance_mode_choice = Binda::Radio + .where( + field_setting_id: Binda::FieldSetting.where( + slug: 'maintenance-mode' + ) + .ids + ) + .first + .choices + .first + expect(maintenance_mode_choice.label).to eq 'disabled' + expect(maintenance_mode_choice.value).to eq 'false' + end + + end + + + describe "executing from an existing application" do + + before(:all) do + ::DatabaseCleaner.strategy = :truncation + ::DatabaseCleaner.clean + + prepare_destination + # @old_stdin = STDIN + STDIN = StringIO.new("mail@admin.com\npassword\nWebsite name\nWebsite description\n") + run_generator + # STDIN = @old_stdin + end + + it "can create a default user" do + expect(Binda::User.where(email: 'mail@admin.com').present?).to be true + end + end + end +end \ No newline at end of file From d3a5bcd6ab1a2b1e7b79c46890739418022c9927 Mon Sep 17 00:00:00 2001 From: Alessandro Barbieri Date: Sat, 10 Feb 2018 03:26:49 +0100 Subject: [PATCH 05/38] NEW FEATURE add sort to field settings --- .../javascripts/binda/components/form_item.js | 109 ++++++++++-- .../binda/components/form_item_repeater.js | 54 +++--- .../javascripts/binda/components/sortable.js | 11 +- .../javascripts/binda/dist/binda.bundle.js | 157 +++++++++++++----- .../components/field_setting_choices.scss | 8 +- .../binda/components/form_item.scss | 78 ++------- .../binda/components/standard-form.scss | 40 ++++- .../binda/components_controller.rb | 27 ++- .../binda/field_groups_controller.rb | 64 +++++-- .../binda/field_settings_controller.rb | 16 +- app/controllers/binda/repeaters_controller.rb | 4 +- .../binda/field_groups/_form_body.html.erb | 46 +---- .../binda/field_groups/_form_header.html.erb | 11 ++ .../binda/field_groups/_form_item.html.erb | 10 +- .../field_groups/_form_new_item.html.erb | 10 ++ .../binda/field_groups/_form_section.html.erb | 44 ++--- .../_form_section_repeater.html.erb | 35 ++-- .../binda/field_groups/_form_sidebar.html.erb | 13 ++ .../form_item/_form_item_editor.html.erb | 11 +- ... _form_item_editor_existing_item.html.erb} | 3 - .../_form_item_editor_new_item.html.erb | 3 + .../form_item/_form_item_header.html.erb | 39 ++--- .../form_item/_form_item_new_editor.html.erb | 8 - .../_form_section_header.html.erb | 44 +++++ .../form_section/_form_section_list.html.erb | 12 ++ .../_form_item_new_repeater.html.erb | 25 +-- .../binda/fieldable/_form_section.html.erb | 17 +- .../fieldable/_form_section_repeater.html.erb | 1 - .../_form_item_repeater_header.html.erb | 7 +- .../_form_item_repeater_list.html.erb | 16 +- ...=> _form_item_repeater_list_item.html.erb} | 15 +- ...form_item_repeater_populated_list.html.erb | 40 ----- config/locales/en.yml | 1 + config/routes.rb | 2 + .../binda/install/install_generator.rb | 1 - spec/features/components_edit_spec.rb | 6 + .../field_groups_edit__editor_spec.rb | 43 +++-- .../generators/binda/setup_generator_spec.rb | 9 +- 38 files changed, 614 insertions(+), 426 deletions(-) create mode 100644 app/views/binda/field_groups/_form_header.html.erb create mode 100644 app/views/binda/field_groups/_form_new_item.html.erb create mode 100644 app/views/binda/field_groups/_form_sidebar.html.erb rename app/views/binda/field_groups/form_item/{_form_item_persisted_editor.html.erb => _form_item_editor_existing_item.html.erb} (88%) create mode 100644 app/views/binda/field_groups/form_item/_form_item_editor_new_item.html.erb delete mode 100644 app/views/binda/field_groups/form_item/_form_item_new_editor.html.erb create mode 100644 app/views/binda/field_groups/form_section/_form_section_header.html.erb create mode 100644 app/views/binda/field_groups/form_section/_form_section_list.html.erb rename app/views/binda/fieldable/form_item_repeater/{_form_item_repeater_empty_list.html.erb => _form_item_repeater_list_item.html.erb} (71%) delete mode 100644 app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb diff --git a/app/assets/javascripts/binda/components/form_item.js b/app/assets/javascripts/binda/components/form_item.js index b99a833a..dfdad548 100644 --- a/app/assets/javascripts/binda/components/form_item.js +++ b/app/assets/javascripts/binda/components/form_item.js @@ -12,7 +12,7 @@ class FormItem { constructor() {} isSet() { - if ($(".form-item").length > 0) { + if ($(".form-item--add-new").length > 0) { return true; } else { return false; @@ -21,15 +21,7 @@ class FormItem { setEvents() { $(document).on("click", ".form-item--add-new", addNewItem); - - $(document).on("click", ".form-item--remove-item-with-js", function(event) { - // Stop default behaviour - event.preventDefault(); - $(this) - .closest(".form-item") - .remove(); - }); - + $(document).on("click", ".form-item--delete-item", deleteItem); $(document).on("click", ".form-item--collapse-btn", collapseToggle); } } @@ -40,9 +32,73 @@ export let _FormItem = new FormItem(); /// COMPONENT HELPER FUNCTIONS ///- - - - - - - - - - - - - - - - - - - - +function addNewItem(event) { + // Stop default behaviour + event.preventDefault(); + // Get the child to clone + let id = $(this).data("id"); + let $list = $("#form-item--field-list-" + id); + let url = $(this).data("url"); + let data = $list.sortable("serialize"); + let params = $(this).data("params"); + if (params) { + data = data.concat(`&${params}`); + } + + $.post(url, data, function(data) { + // Get repaeter code from Rails + // Due to the Rails way of creating nested forms it's necessary to + // create the nested item inside a different new form, then get just + // the code contained between the two SPLIT comments + let parts = data.split(""); + let newItem = parts[1]; + setupAndAppend(newItem, $list); + }); +} + +function setupAndAppend(newItem, $list) { + // Append the item + $list.prepend(newItem); + let collapsable = $list.find(".form-item--collapsable").get(0); + + // Prepare collapsable animation + collapsable.style.maxHeight = "0px"; + + // Setup TinyMCE for the newly created item + var textarea_editor_id = $list + .find("textarea") + .last("textarea") + .attr("id"); + tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); + + // Prepare collapsable stack animation + $(collapsable) + .find(".form-item--collapsable-stack") + .each(function() { + if (!$list.hasClass("sortable--enabled")) { + // Prepare field stack for collapsable animation + this.style.maxHeight = collapsable.scrollHeight + "px"; + } + }); + + // Resize the editor (is it needed with the new configuration?) + // _FormItemEditor.resize() + + // Update select input for Select2 plugin + setupSelect2($list.find("select")); + + // Refresh Sortable to update the added item with Sortable features + $list.sortable("refresh"); + + // Run animation 500ms after previous style declaration (see above) otherwise animation doesn't get triggered + setTimeout(function() { + collapsable.style.maxHeight = collapsable.scrollHeight + "px"; + }, 50); +} + // This function could be improved as it generates an issue with // input ids which are duplicated after the entire target has been cloned -function addNewItem(event) { +function DEPRECATEDaddNewItem(event) { // Stop default behaviour event.preventDefault(); // Get the child to clone @@ -102,12 +158,39 @@ function collapseToggle(event) { let $collapsable = $(this).closest(".form-item--collapsable"); if ($collapsable.hasClass("form-item--collapsed")) { - $collapsable.find(".form-item--repeater-fields").each(open); + $collapsable.find(".form-item--collapsable-stack").each(open); $collapsable.find(".form-item--editor").each(open); $collapsable.removeClass("form-item--collapsed"); } else { - $collapsable.find(".form-item--repeater-fields").each(close); + $collapsable.find(".form-item--collapsable-stack").each(close); $collapsable.find(".form-item--editor").each(close); $collapsable.addClass("form-item--collapsed"); } } + +function deleteItem(event) { + // Stop default behaviour + event.preventDefault(); + let record_id = $(this).data("id"); + let targetId = `#form-item-${record_id}` + let target = $(targetId).get(0); + // As max-height isn't set you need to set it manually before changing it, + // otherwise the animation doesn't get triggered + target.style.maxHeight = target.scrollHeight + "px"; + // Change max-height after 50ms to trigger css animation + setTimeout(function() { + target.style.maxHeight = "0px"; + }, 50); + + $.ajax({ + url: $(this).attr("href"), + data: { id: record_id, target_id: targetId, isAjax: true }, + method: "DELETE" + }).done(data => { + // Make sure the animation completes before removing the item (it should last 600ms + 50ms) + setTimeout(function() { + $(data.target_id).remove(); + }, 700); + }); + // TODO add a fallback if request fails +} diff --git a/app/assets/javascripts/binda/components/form_item_repeater.js b/app/assets/javascripts/binda/components/form_item_repeater.js index 2f437f15..5d3994d6 100644 --- a/app/assets/javascripts/binda/components/form_item_repeater.js +++ b/app/assets/javascripts/binda/components/form_item_repeater.js @@ -17,21 +17,7 @@ class FormItemRepeater { } setEvents() { - $(document).on( - "click", - ".form-item--repeater-section--add-new", - addNewItem - ); - - $(document).on("click", ".form-item--remove-item-with-js", function(event) { - // Stop default behaviour - event.preventDefault(); - $(this) - .parent(".form-item--repeater-section") - .remove(); - _FormItemEditor.resize(); - }); - + $(document).on("click", ".form-item--repeater-section--add-new", addNewItem); $(document).on("click", ".form-item--delete-repeater-item", deleteRepeter); } } @@ -49,7 +35,10 @@ function addNewItem(event) { let id = $(this).data("id"); let $list = $("#form-item--repeater-setting-" + id); let url = $(this).data("url"); - $.post(url, { repeater_setting_id: id }, function(data) { + let data = $list.sortable("serialize"); + data = data.concat(`&repeater_setting_id=${id}`); + + $.post(url, data, function(data) { // Get repaeter code from Rails // Due to the Rails way of creating nested forms it's necessary to // create the nested item inside a different new form, then get just @@ -65,17 +54,8 @@ function setupAndAppend(newRepeater, $list) { $list.prepend(newRepeater); let new_repeater_item = $list.find(".form-item--repeater").get(0); - // Prepare animation - new_repeater_item.style.maxHeight = 0; - - // Group fields if sotrable is enabled - if ($list.hasClass("sortable--enabled")) { - $(new_repeater_item) - .find(".form-item--repeater-fields") - .each(function() { - this.style.maxHeight = 0 + "px"; - }); - } + // Prepare repeater animation + new_repeater_item.style.maxHeight = "0px"; // Setup TinyMCE for the newly created item var textarea_editor_id = $list @@ -84,6 +64,16 @@ function setupAndAppend(newRepeater, $list) { .attr("id"); tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); + // // Prepare collapsable stack animation + // $(new_repeater_item) + // .find(".form-item--collapsable-stack") + // .each(function() { + // if (!$list.hasClass("sortable--enabled")) { + // // Prepare field stack for collapsable animation + // this.style.maxHeight = new_repeater_item.scrollHeight + "px"; + // } + // }); + // Resize the editor (is it needed with the new configuration?) // _FormItemEditor.resize() @@ -104,7 +94,8 @@ function deleteRepeter(event) { event.preventDefault(); let record_id = $(this).data("id"); - let target = $("#repeater_" + record_id).get(0); + let targetId = `#repeater_${record_id}` + let target = $(targetId).get(0); // As max-height isn't set you need to set it manually before changing it, // otherwise the animation doesn't get triggered target.style.maxHeight = target.scrollHeight + "px"; @@ -115,13 +106,12 @@ function deleteRepeter(event) { $.ajax({ url: $(this).attr("href"), - data: { id: record_id, isAjax: true }, + data: { id: record_id, target_id: targetId, isAjax: true }, method: "DELETE" - }).done(() => { + }).done(data => { // Make sure the animation completes before removing the item (it should last 600ms + 50ms) setTimeout(function() { - $(target).remove(); + $(data.target_id).remove(); }, 700); - // _FormItemEditor.resize() }); } diff --git a/app/assets/javascripts/binda/components/sortable.js b/app/assets/javascripts/binda/components/sortable.js index 3e45bc59..cf45e51c 100644 --- a/app/assets/javascripts/binda/components/sortable.js +++ b/app/assets/javascripts/binda/components/sortable.js @@ -33,22 +33,21 @@ export default function() { } // Add event to any sortable toggle button - // TODO: make this event available to element which aren't standard form repeaters $(document).on( "click", - ".standard-form--repeater .sortable--toggle", + ".standard-form--repeater .sortable--toggle, .standard-form--field-group .sortable--toggle", toggleSortable ); } function setupSortableToggle() { $(".sortable--toggle").each(function() { - let id = "#" + $(this).data("repeater-id"); + let id = "#" + $(this).data("sortable-target-id"); $(id) .find(".form-item--collapsable") .addClass("form-item--collapsed"); $(id) - .find(".form-item--repeater-fields") + .find(".form-item--collapsable-stack") .each(close); }); } @@ -63,12 +62,12 @@ function open() { function toggleSortable(event) { event.preventDefault(); - let id = "#" + $(this).data("repeater-id"); + let id = "#" + $(this).data("sortable-target-id"); if ($(id).hasClass("sortable--disabled")) { $(id).sortable("enable"); $(id) - .find(".form-item--repeater-fields") + .find(".form-item--collapsable-stack") .each(close); $(id) .find(".form-item--collapsable") diff --git a/app/assets/javascripts/binda/dist/binda.bundle.js b/app/assets/javascripts/binda/dist/binda.bundle.js index 5662c30c..2ae0d4f3 100644 --- a/app/assets/javascripts/binda/dist/binda.bundle.js +++ b/app/assets/javascripts/binda/dist/binda.bundle.js @@ -265,7 +265,7 @@ var FormItem = function () { _createClass(FormItem, [{ key: "isSet", value: function isSet() { - if ($(".form-item").length > 0) { + if ($(".form-item--add-new").length > 0) { return true; } else { return false; @@ -275,13 +275,7 @@ var FormItem = function () { key: "setEvents", value: function setEvents() { $(document).on("click", ".form-item--add-new", addNewItem); - - $(document).on("click", ".form-item--remove-item-with-js", function (event) { - // Stop default behaviour - event.preventDefault(); - $(this).closest(".form-item").remove(); - }); - + $(document).on("click", ".form-item--delete-item", deleteItem); $(document).on("click", ".form-item--collapse-btn", collapseToggle); } }]); @@ -295,9 +289,68 @@ var _FormItem = new FormItem(); /// COMPONENT HELPER FUNCTIONS ///- - - - - - - - - - - - - - - - - - - - +function addNewItem(event) { + // Stop default behaviour + event.preventDefault(); + // Get the child to clone + var id = $(this).data("id"); + var $list = $("#form-item--field-list-" + id); + var url = $(this).data("url"); + var data = $list.sortable("serialize"); + var params = $(this).data("params"); + if (params) { + data = data.concat("&" + params); + } + + $.post(url, data, function (data) { + // Get repaeter code from Rails + // Due to the Rails way of creating nested forms it's necessary to + // create the nested item inside a different new form, then get just + // the code contained between the two SPLIT comments + var parts = data.split(""); + var newItem = parts[1]; + setupAndAppend(newItem, $list); + }); +} + +function setupAndAppend(newItem, $list) { + // Append the item + $list.prepend(newItem); + var collapsable = $list.find(".form-item--collapsable").get(0); + + // Prepare collapsable animation + collapsable.style.maxHeight = "0px"; + + // Setup TinyMCE for the newly created item + var textarea_editor_id = $list.find("textarea").last("textarea").attr("id"); + tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); + + // Prepare collapsable stack animation + $(collapsable).find(".form-item--collapsable-stack").each(function () { + if (!$list.hasClass("sortable--enabled")) { + // Prepare field stack for collapsable animation + this.style.maxHeight = collapsable.scrollHeight + "px"; + } + }); + + // Resize the editor (is it needed with the new configuration?) + // _FormItemEditor.resize() + + // Update select input for Select2 plugin + Object(__WEBPACK_IMPORTED_MODULE_1__select2__["b" /* setupSelect2 */])($list.find("select")); + + // Refresh Sortable to update the added item with Sortable features + $list.sortable("refresh"); + + // Run animation 500ms after previous style declaration (see above) otherwise animation doesn't get triggered + setTimeout(function () { + collapsable.style.maxHeight = collapsable.scrollHeight + "px"; + }, 50); +} + // This function could be improved as it generates an issue with // input ids which are duplicated after the entire target has been cloned -function addNewItem(event) { +function DEPRECATEDaddNewItem(event) { // Stop default behaviour event.preventDefault(); // Get the child to clone @@ -352,16 +405,43 @@ function collapseToggle(event) { var $collapsable = $(this).closest(".form-item--collapsable"); if ($collapsable.hasClass("form-item--collapsed")) { - $collapsable.find(".form-item--repeater-fields").each(open); + $collapsable.find(".form-item--collapsable-stack").each(open); $collapsable.find(".form-item--editor").each(open); $collapsable.removeClass("form-item--collapsed"); } else { - $collapsable.find(".form-item--repeater-fields").each(close); + $collapsable.find(".form-item--collapsable-stack").each(close); $collapsable.find(".form-item--editor").each(close); $collapsable.addClass("form-item--collapsed"); } } +function deleteItem(event) { + // Stop default behaviour + event.preventDefault(); + var record_id = $(this).data("id"); + var targetId = "#form-item-" + record_id; + var target = $(targetId).get(0); + // As max-height isn't set you need to set it manually before changing it, + // otherwise the animation doesn't get triggered + target.style.maxHeight = target.scrollHeight + "px"; + // Change max-height after 50ms to trigger css animation + setTimeout(function () { + target.style.maxHeight = "0px"; + }, 50); + + $.ajax({ + url: $(this).attr("href"), + data: { id: record_id, target_id: targetId, isAjax: true }, + method: "DELETE" + }).done(function (data) { + // Make sure the animation completes before removing the item (it should last 600ms + 50ms) + setTimeout(function () { + $(data.target_id).remove(); + }, 700); + }); + // TODO add a fallback if request fails +} + /***/ }), /* 4 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -399,14 +479,6 @@ var FormItemRepeater = function () { key: "setEvents", value: function setEvents() { $(document).on("click", ".form-item--repeater-section--add-new", addNewItem); - - $(document).on("click", ".form-item--remove-item-with-js", function (event) { - // Stop default behaviour - event.preventDefault(); - $(this).parent(".form-item--repeater-section").remove(); - __WEBPACK_IMPORTED_MODULE_0__form_item_editor__["a" /* _FormItemEditor */].resize(); - }); - $(document).on("click", ".form-item--delete-repeater-item", deleteRepeter); } }]); @@ -427,7 +499,10 @@ function addNewItem(event) { var id = $(this).data("id"); var $list = $("#form-item--repeater-setting-" + id); var url = $(this).data("url"); - $.post(url, { repeater_setting_id: id }, function (data) { + var data = $list.sortable("serialize"); + data = data.concat("&repeater_setting_id=" + id); + + $.post(url, data, function (data) { // Get repaeter code from Rails // Due to the Rails way of creating nested forms it's necessary to // create the nested item inside a different new form, then get just @@ -443,20 +518,23 @@ function setupAndAppend(newRepeater, $list) { $list.prepend(newRepeater); var new_repeater_item = $list.find(".form-item--repeater").get(0); - // Prepare animation - new_repeater_item.style.maxHeight = 0; - - // Group fields if sotrable is enabled - if ($list.hasClass("sortable--enabled")) { - $(new_repeater_item).find(".form-item--repeater-fields").each(function () { - this.style.maxHeight = 0 + "px"; - }); - } + // Prepare repeater animation + new_repeater_item.style.maxHeight = "0px"; // Setup TinyMCE for the newly created item var textarea_editor_id = $list.find("textarea").last("textarea").attr("id"); tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); + // // Prepare collapsable stack animation + // $(new_repeater_item) + // .find(".form-item--collapsable-stack") + // .each(function() { + // if (!$list.hasClass("sortable--enabled")) { + // // Prepare field stack for collapsable animation + // this.style.maxHeight = new_repeater_item.scrollHeight + "px"; + // } + // }); + // Resize the editor (is it needed with the new configuration?) // _FormItemEditor.resize() @@ -477,7 +555,8 @@ function deleteRepeter(event) { event.preventDefault(); var record_id = $(this).data("id"); - var target = $("#repeater_" + record_id).get(0); + var targetId = "#repeater_" + record_id; + var target = $(targetId).get(0); // As max-height isn't set you need to set it manually before changing it, // otherwise the animation doesn't get triggered target.style.maxHeight = target.scrollHeight + "px"; @@ -488,14 +567,13 @@ function deleteRepeter(event) { $.ajax({ url: $(this).attr("href"), - data: { id: record_id, isAjax: true }, + data: { id: record_id, target_id: targetId, isAjax: true }, method: "DELETE" - }).done(function () { + }).done(function (data) { // Make sure the animation completes before removing the item (it should last 600ms + 50ms) setTimeout(function () { - $(target).remove(); + $(data.target_id).remove(); }, 700); - // _FormItemEditor.resize() }); } @@ -1225,15 +1303,14 @@ var sortableOptions = { } // Add event to any sortable toggle button - // TODO: make this event available to element which aren't standard form repeaters - $(document).on("click", ".standard-form--repeater .sortable--toggle", toggleSortable); + $(document).on("click", ".standard-form--repeater .sortable--toggle, .standard-form--field-group .sortable--toggle", toggleSortable); }); function setupSortableToggle() { $(".sortable--toggle").each(function () { - var id = "#" + $(this).data("repeater-id"); + var id = "#" + $(this).data("sortable-target-id"); $(id).find(".form-item--collapsable").addClass("form-item--collapsed"); - $(id).find(".form-item--repeater-fields").each(close); + $(id).find(".form-item--collapsable-stack").each(close); }); } @@ -1247,11 +1324,11 @@ function open() { function toggleSortable(event) { event.preventDefault(); - var id = "#" + $(this).data("repeater-id"); + var id = "#" + $(this).data("sortable-target-id"); if ($(id).hasClass("sortable--disabled")) { $(id).sortable("enable"); - $(id).find(".form-item--repeater-fields").each(close); + $(id).find(".form-item--collapsable-stack").each(close); $(id).find(".form-item--collapsable").addClass("form-item--collapsed"); } else { $(id).sortable("disable"); diff --git a/app/assets/stylesheets/binda/components/field_setting_choices.scss b/app/assets/stylesheets/binda/components/field_setting_choices.scss index 8708ecf3..cf78ba9d 100644 --- a/app/assets/stylesheets/binda/components/field_setting_choices.scss +++ b/app/assets/stylesheets/binda/components/field_setting_choices.scss @@ -20,7 +20,10 @@ } .field-setting-choices--add-choice { - float: right; + position: absolute; + top: 0; + right: 0; + padding: 20px 12px 8px; } .form-item--editor .field-setting-choices--choice { @@ -48,8 +51,7 @@ .field-setting-choices--choice-label, .field-setting-choices--choice-value { - position: relative; - float: left; + display: inline-block; width: calc(50% - 16px); } diff --git a/app/assets/stylesheets/binda/components/form_item.scss b/app/assets/stylesheets/binda/components/form_item.scss index 5ba87f9e..9611e116 100644 --- a/app/assets/stylesheets/binda/components/form_item.scss +++ b/app/assets/stylesheets/binda/components/form_item.scss @@ -1,8 +1,6 @@ .form-item { display: block; overflow-y: hidden; - transition: max-height 0.6s ease-out; - @extend .clearfix; } @@ -11,9 +9,14 @@ } .form-item--half-size { - position: relative; - float: left; - width: 50%; + // OLD SOLUTION (nicer, but can't be used with max-height=0 and overflow-y=hidden) + // position: relative; + // float: left; + // width: 50%; + + // NEW SOLUTION (not very nice but plays nicely with max-height=0 and overflow-y=hidden) + display: inline-block; + width: calc(50% - 3px); // 5px are for the weird issue caused by non-blank space around the inline-block elements .form-group { border-top: none !important; @@ -33,12 +36,6 @@ } } -.form-item--new { - position: absolute; - top: 101%; - max-height: 0; -} - .form-item--select-input { display: inline-block; width: 240px; @@ -78,14 +75,6 @@ transform: rotate(180deg); } -.form-item--editor { - overflow-y: hidden; - // max-height: is set via javascript in app/assets/javscripts/components/form_item_editor.js; - // default is 0 - max-height: 0; - transition: max-height 0.6s ease-out; -} - .form-item--repeater { overflow-y: hidden; // inherit transitions from sortable and add max-height @@ -102,54 +91,15 @@ transition: all 0.6s ease; } -.font-item--collapsed .form-item--repeater-fields { - margin-bottom: 8px; +.form-item--collapsable { + transition: box-shadow 0.3s ease, transform 0.3s ease, max-height 0.6s ease-out !important; } -.form-item--repeater-section { - overflow-x: hidden; // This ensure that the div doesn't become wider when draggin sortable items to the right - margin-bottom: 0; - - ul.sortable > li { - margin-top: -1px; // this solve the issue of a 2px border between 2 repeaters - border: 1px solid $color-gray-lighter; - } - - ul.sortable > li > * { - display: block; - } - - ul.sortable { - margin-bottom: 0; - } - - .ui-sortable-handle:hover { - background-color: transparent; - } - - .sortable--toggle-text:first-child { - display: none; - } - - .sortable--toggle { - float: right; - margin: 0; - } - - .sortable--enabled { - .form-item--repeater-fields { - margin: 0; - padding: 0; - } - } - - .form-item--new { - position: absolute; - top: 101%; - max-height: 0; - } +.font-item--collapsed .form-item--collapsable-stack { + margin-bottom: 8px; } +.form-item--add-new, .form-item--repeater-section--add-new { float: right; margin-right: 8px; @@ -172,12 +122,14 @@ .form-item--collapse-btn, .form-item--delete-repeater-item, +.form-item--delete-item, .form-item--edit-item { float: right; margin-top: 8px; margin-right: 8px; } +a.form-item--delete-item, a.form-item--delete-repeater-item { cursor: pointer; transition: color 0.2s ease; diff --git a/app/assets/stylesheets/binda/components/standard-form.scss b/app/assets/stylesheets/binda/components/standard-form.scss index e47e9c87..ddb7d953 100644 --- a/app/assets/stylesheets/binda/components/standard-form.scss +++ b/app/assets/stylesheets/binda/components/standard-form.scss @@ -178,5 +178,43 @@ padding: 12px; } -.standard-form--choices-header { +.standard-form--repeater, +.standard-form--field-group { + overflow-x: hidden; // This ensure that the div doesn't become wider when draggin sortable items to the right + margin-bottom: 0; + + ul.sortable > li { + margin-top: -1px; // this solve the issue of a 2px border between 2 repeaters + border: 1px solid $color-gray-lighter; + } + + ul.sortable > li > * { + display: block; + } + + ul.sortable { + margin-bottom: 0; + } + + .ui-sortable-handle:hover { + background-color: transparent; + } + + .sortable--toggle-text:first-child { + display: none; + } + + .sortable--toggle { + float: right; + margin: 0; + } + + .sortable--enabled { + .form-item--collapsable-stack { + margin: 0; + padding: 0; + max-height: 0; + } + } } + diff --git a/app/controllers/binda/components_controller.rb b/app/controllers/binda/components_controller.rb index aab2f1b2..93545614 100644 --- a/app/controllers/binda/components_controller.rb +++ b/app/controllers/binda/components_controller.rb @@ -57,15 +57,24 @@ def destroy def new_repeater @repeater_setting = FieldSetting.find( params[:repeater_setting_id] ) - position = @instance.repeaters.find_all{|r| r.field_setting_id=@repeater_setting.id }.length + 1 - @repeater = @instance.repeaters.create( field_setting: @repeater_setting, position: position ) + @repeater = @instance.repeaters.create( field_setting: @repeater_setting ) + # Put new repeater to first position, then store all the other ones + if params[:repeater].nil? + repeaters = [ + @repeater.id.to_s, + *@instance.repeaters.select{|r| r.field_setting_id=@repeater_setting.id }.map(&:id) + ] + else + repeaters = [ + @repeater.id.to_s, + *params[:repeater]] + end + sort_repeaters_by(repeaters) render 'binda/fieldable/_form_item_new_repeater', layout: false end def sort_repeaters - params[:repeater].each_with_index do |id, i| - Repeater.find( id ).update({ position: i + 1 }) - end + sort_repeaters_by(params[:repeater]) render json: { id: "##{params[:id]}" }, status: 200 end @@ -111,6 +120,14 @@ def component_params {categories_attributes: [ :id, :category_id ]}, *fieldable_params ) end + # Sort repeaters following the order with which are listed in the array provided as a argument. + # + # @param repeaters [Array] the list of ids of the repeaters + def sort_repeaters_by(repeaters) + repeaters.each_with_index do |id, i| + Repeater.find( id ).update!({ position: i + 1 }) + end + end end end diff --git a/app/controllers/binda/field_groups_controller.rb b/app/controllers/binda/field_groups_controller.rb index 6ba11902..1c4a0bbf 100644 --- a/app/controllers/binda/field_groups_controller.rb +++ b/app/controllers/binda/field_groups_controller.rb @@ -3,7 +3,7 @@ module Binda class FieldGroupsController < ApplicationController before_action :set_structure - before_action :set_field_group, only: [:show, :edit, :update, :destroy] + before_action :set_field_group, only: [:show, :edit, :update, :destroy, :new_field_setting] def index redirect_to structure_field_group_path( @structure.slug ) @@ -33,7 +33,6 @@ def create def update # Add nested classes - add_new_field_settings add_new_choices check_if_needs_to_update_choices @@ -47,11 +46,51 @@ def update end def destroy - @field_group.destroy + @field_group.destroy! reset_field_settings_cache redirect_to structure_path( @structure.slug ), notice: 'Field group was successfully destroyed.' end + def sort + params[:field_group].each_with_index do |id, i| + FieldGroup.find( id ).update_column('position', i + 1) # use update_column to skip callbacks (which leads to huge useless memory consumption) + end + render json: { id: "##{params[:id]}" }, status: 200 + end + + def sort_field_settings + params["form-item"].each_with_index do |id, i| + FieldSetting.find( id ).update_column('position', i + 1) # use update_column to skip callbacks (which leads to huge useless memory consumption) + end + render json: { id: "##{params[:id]}" }, status: 200 + end + + def new_field_setting + # We set some default values in order to be able to save the field setting + # (if field setting isn't save it makes impossible to sort the order) + @field_setting = FieldSetting.new( + name: "#{I18n.t('binda.field_setting.new')}", + field_group_id: @field_group.id, + field_type: 'string' + ) + @field_setting[:ancestry] = params[:ancestry] + @field_setting.save! + # Put new repeater to first position, then store all the other ones + if params["form-item"].nil? + field_settings = [ + @field_setting.id.to_s, + *@field_group.field_settings.select{|fs| fs.ancestry == @field_setting.ancestry }.map(&:id) + ] + else + field_settings = [ + @field_setting.id.to_s, + *params["form-item"] + ] + end + sort_field_setting_by(field_settings) + render 'binda/field_groups/_form_new_item', layout: false + end + private # Use callbacks to share common setup or constraints between actions. def set_structure @@ -75,16 +114,6 @@ def reset_field_settings_cache FieldSetting.reset_field_settings_array end - def add_new_field_settings - # Create new fields if any - new_params[:new_field_settings].each do |field_setting| - next if field_setting[:name].blank? - new_field_setting = @field_group.field_settings.create( field_setting ) - next if new_field_setting - return redirect_to edit_structure_field_group_path( @structure.slug, @field_group.slug ), flash: { error: new_field_setting.errors } - end - end - def add_new_choices # Create new fields if any return if new_params[:new_choices].nil? @@ -118,5 +147,14 @@ def update_field_setting_choices field_setting_params end end end + + # Sort field settings following the order with which are listed in the array provided as a argument. + # + # @param field_settings [Array] the list of ids of the field settings + def sort_field_setting_by(field_settings) + field_settings.each_with_index do |id, i| + FieldSetting.find( id ).update!({ position: i + 1 }) + end + end end end diff --git a/app/controllers/binda/field_settings_controller.rb b/app/controllers/binda/field_settings_controller.rb index fe90c0b9..eaa4d426 100644 --- a/app/controllers/binda/field_settings_controller.rb +++ b/app/controllers/binda/field_settings_controller.rb @@ -40,8 +40,20 @@ def update end def destroy - @field_setting.destroy - redirect_to structure_field_group_path( @structure, @field_group ), notice: 'Field setting and all dependent content were successfully destroyed.' + @field_setting.destroy! + FieldSetting.reset_field_settings_array + if params[:isAjax] + render json: { target_id: params[:target_id] }, status: 200 + else + redirect_to structure_field_group_path( @structure, @field_group ), notice: 'Field setting and all dependent content were successfully destroyed.' + end + end + + def sort + params[:field_setting].each_with_index do |id, i| + FieldSetting.find( id ).update_column('position', i + 1) # use update_column to skip callbacks (which leads to huge useless memory consumption) + end + render json: { id: "##{params[:id]}" }, status: 200 end private diff --git a/app/controllers/binda/repeaters_controller.rb b/app/controllers/binda/repeaters_controller.rb index debd95e8..bc5a90a9 100644 --- a/app/controllers/binda/repeaters_controller.rb +++ b/app/controllers/binda/repeaters_controller.rb @@ -35,10 +35,10 @@ def update # DELETE /repeaters/1 def destroy - @repeater.destroy + @repeater.destroy! # redirect_to repeaters_url, notice: 'Repeater was successfully destroyed.' if params['isAjax'] - head :ok + render json: { target_id: params[:target_id] }, status: 200 else redirect_to :back, notice: 'Field group was successfully destroyed.' end diff --git a/app/views/binda/field_groups/_form_body.html.erb b/app/views/binda/field_groups/_form_body.html.erb index cd285703..98697fca 100644 --- a/app/views/binda/field_groups/_form_body.html.erb +++ b/app/views/binda/field_groups/_form_body.html.erb @@ -4,52 +4,14 @@ <%= f.error_notification %> <%= render 'layouts/binda/form_errors', f: f %>
    -
    -
    - <%= "#{ t :details }" %> -
    - <%= f.input :name %> - <% if @field_group.slug.nil? %> - <%= f.input :slug, as: :hidden %> - <% else %> - <%= f.input :slug %> - <% end %> -
    -
    -
    -
    - <%= "#{ t 'binda.field_setting.plural' }" %> -
    - <%= render "form_section", f: f %> -
    -
    + <%= render "form_header", f: f %> + <%= render "form_section", f: f %> <% @field_group.field_settings.where( field_type: 'repeater' ).order( :position, :id ).each do |repeater| %> -
    -
    -
    -
    - <%= "#{ t('binda.repeater_field_settings', arg1: repeater.name ) }" %> -
    - <%= render "form_section_repeater", f: f, repeater: repeater %> -
    -
    -
    + <%= render "form_section_repeater", f: f, repeater: repeater %> <% end %>
    -
    -
    - <%= "#{ t :setting }".pluralize %> -
    -
    - <%= button_tag 'Save changes'.html_safe, { - class: 'b-btn b-btn-primary b-btn-settings', - id: 'save', - 'data-entries-number': get_entries_number, - 'data-instance-type': @structure.instance_type.pluralize - } %> -
    -
    + <%= render "form_sidebar" %>
    <% end %>
    \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_header.html.erb b/app/views/binda/field_groups/_form_header.html.erb new file mode 100644 index 00000000..b38a95f3 --- /dev/null +++ b/app/views/binda/field_groups/_form_header.html.erb @@ -0,0 +1,11 @@ +
    +
    + <%= "#{ t :details }" %> +
    + <%= f.input :name %> + <% if @field_group.slug.nil? %> + <%= f.input :slug, as: :hidden %> + <% else %> + <%= f.input :slug %> + <% end %> +
    \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_item.html.erb b/app/views/binda/field_groups/_form_item.html.erb index f1f340c3..8a49bf81 100644 --- a/app/views/binda/field_groups/_form_item.html.erb +++ b/app/views/binda/field_groups/_form_item.html.erb @@ -1,7 +1,3 @@ -
    - <%= ff.input :field_group_id, as: :hidden %> - <%= ff.input :id, as: :hidden %> - <%= ff.input :ancestry, as: :hidden unless ff.object.is_root? %> - <%= render 'binda/field_groups/form_item/form_item_header', ff: ff %> - <%= render 'binda/field_groups/form_item/form_item_editor', f: f, ff: ff %> -
    \ No newline at end of file +<%# DON'T USE ff.input :position otherwise it will override the order set by Binda via ajax when adding a new item %> +<%= render 'binda/field_groups/form_item/form_item_header', ff: ff %> +<%= render 'binda/field_groups/form_item/form_item_editor', f: f, ff: ff, isNew: isNew %> \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_new_item.html.erb b/app/views/binda/field_groups/_form_new_item.html.erb new file mode 100644 index 00000000..07b6876a --- /dev/null +++ b/app/views/binda/field_groups/_form_new_item.html.erb @@ -0,0 +1,10 @@ +<%= simple_form_for [@field_group.structure, @field_group], defaults: { label: false }, html: { multipart: true } do |f| %> + + <%= f.simple_fields_for "field_settings_attributes[]", @field_setting do |ff| %> +
  • + <%= render 'form_item', f: f, ff: ff, isNew: true %> +
  • + <% end %> + +<% end %> \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_section.html.erb b/app/views/binda/field_groups/_form_section.html.erb index ea34cdc5..652d4606 100644 --- a/app/views/binda/field_groups/_form_section.html.erb +++ b/app/views/binda/field_groups/_form_section.html.erb @@ -1,30 +1,20 @@ <%# This form is only for settings which are not part of any repeater. %> <%# This is done using `where( ancestry: nil )` %> -<%= f.simple_fields_for :field_settings, f.object.field_settings.where( ancestry: nil ).order( :position, :name ) do |ff| %> -
    "> - <%= render 'form_item', f: f, ff: ff %> + + \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_section_repeater.html.erb b/app/views/binda/field_groups/_form_section_repeater.html.erb index 9c8a2917..1f43368a 100644 --- a/app/views/binda/field_groups/_form_section_repeater.html.erb +++ b/app/views/binda/field_groups/_form_section_repeater.html.erb @@ -1,20 +1,19 @@ -<%= f.simple_fields_for :field_settings, repeater.children.order( :position, :name ) do |ff| %> -
    "> - <%= render 'form_item', f: f, ff: ff %> -
    -<% end %> -<%= f.simple_fields_for 'new_field_settings[]', repeater.children.build( field_group: @field_group ) do |ff| %> -
    - <%= render 'form_item', f: f, ff: ff %> +
    +
    +
    +
    + <%= "#{ t('binda.repeater_field_settings', arg1: repeater.name ) }" %> +
    +
    + <%= render 'binda/field_groups/form_section/form_section_header', f: f, repeater: repeater %> + <%= render 'binda/field_groups/form_section/form_section_list', f: f, repeater: repeater %> +
    + <% if @field_group.slug.nil? %> +

    + <%= t( :hint_create_parent_before_child, arg1: "#{ t :field_group }", arg2: "#{ t :field_setting }" ).capitalize %> +

    + <% end %> +
    -<% end %> - \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_sidebar.html.erb b/app/views/binda/field_groups/_form_sidebar.html.erb new file mode 100644 index 00000000..a15d5200 --- /dev/null +++ b/app/views/binda/field_groups/_form_sidebar.html.erb @@ -0,0 +1,13 @@ +
    +
    + <%= "#{ t :setting }".pluralize %> +
    +
    + <%= button_tag 'Save changes'.html_safe, { + class: 'b-btn b-btn-primary b-btn-settings', + id: 'save', + 'data-entries-number': get_entries_number, + 'data-instance-type': @structure.instance_type.pluralize + } %> +
    +
    diff --git a/app/views/binda/field_groups/form_item/_form_item_editor.html.erb b/app/views/binda/field_groups/form_item/_form_item_editor.html.erb index 0fae0235..512b27d2 100644 --- a/app/views/binda/field_groups/form_item/_form_item_editor.html.erb +++ b/app/views/binda/field_groups/form_item/_form_item_editor.html.erb @@ -1,14 +1,17 @@ -
    +
    <%= ff.input :name, as: :string, input_html: { class: "form-item--input" } %>
    - <% if ff.object.new_record? %> - <%= render 'binda/field_groups/form_item/form_item_new_editor', ff: ff %> + <% if isNew %> + <%= render 'binda/field_groups/form_item/form_item_editor_new_item', ff: ff %> <% else %> - <%= render 'binda/field_groups/form_item/form_item_persisted_editor', ff: ff %> + <%= render 'binda/field_groups/form_item/form_item_editor_existing_item', ff: ff %> <% if %w(radio selection checkbox).include?(ff.object.field_type) %> <%= render 'binda/field_groups/form_item/form_item_choice_editor', f: f, ff: ff %> <% end %> <% end %> + <%= ff.input :field_group_id, as: :hidden %> + <%= ff.input :id, as: :hidden %> + <%= ff.input :ancestry, as: :hidden unless ff.object.is_root? %>
    \ No newline at end of file diff --git a/app/views/binda/field_groups/form_item/_form_item_persisted_editor.html.erb b/app/views/binda/field_groups/form_item/_form_item_editor_existing_item.html.erb similarity index 88% rename from app/views/binda/field_groups/form_item/_form_item_persisted_editor.html.erb rename to app/views/binda/field_groups/form_item/_form_item_editor_existing_item.html.erb index a859f71e..dad9d209 100644 --- a/app/views/binda/field_groups/form_item/_form_item_persisted_editor.html.erb +++ b/app/views/binda/field_groups/form_item/_form_item_editor_existing_item.html.erb @@ -11,9 +11,6 @@
    <%= ff.input :description, input_html: { class: "form-item--input" } %>
    -
    - <%= ff.input :position, as: :integer, input_html: { class: "form-item--input" } %> -
    <% if ff.object.field_type == 'relation' %>
    <%= ff.input :accepted_structure_ids, diff --git a/app/views/binda/field_groups/form_item/_form_item_editor_new_item.html.erb b/app/views/binda/field_groups/form_item/_form_item_editor_new_item.html.erb new file mode 100644 index 00000000..9c57485b --- /dev/null +++ b/app/views/binda/field_groups/form_item/_form_item_editor_new_item.html.erb @@ -0,0 +1,3 @@ +
    + <%= ff.input :field_type, as: :select, collection: get_field_types, include_blank: false, prompt: t(:select_field_type), input_html: { class: 'form-item--select-input' } %> +
    diff --git a/app/views/binda/field_groups/form_item/_form_item_header.html.erb b/app/views/binda/field_groups/form_item/_form_item_header.html.erb index 4344359a..008146b0 100644 --- a/app/views/binda/field_groups/form_item/_form_item_header.html.erb +++ b/app/views/binda/field_groups/form_item/_form_item_header.html.erb @@ -1,25 +1,16 @@ -
    - <% if ff.object.slug.nil? %> - - - <%= t('binda.delete').capitalize %> - -

    <%= t('binda.new_item_in_repeater', { arg1: '' })%>

    - <% else %> - - <%= t('binda.collapse') %> - <%= t('binda.expand') %> - - - - <%= t('binda.delete').capitalize %> - -

    - <%= ff.object.name %> (<%= ff.object.field_type.humanize.capitalize %>) -

    - <% end %> +
    + + <%= t('binda.collapse') %> + <%= t('binda.expand') %> + + + + <%= t('binda.delete').capitalize %> + +

    + <%= ff.object.name %> (<%= ff.object.field_type.humanize.capitalize %>) +

    \ No newline at end of file diff --git a/app/views/binda/field_groups/form_item/_form_item_new_editor.html.erb b/app/views/binda/field_groups/form_item/_form_item_new_editor.html.erb deleted file mode 100644 index 7453f92b..00000000 --- a/app/views/binda/field_groups/form_item/_form_item_new_editor.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -<%= ff.input :slug, as: :hidden, input_html: { class: "form-item--input" } %> -
    - <% if ff.object.field_type.nil? %> - <%= ff.input :field_type, as: :select, collection: get_field_types, include_blank: false, prompt: t(:select_field_type), input_html: { class: 'form-item--select-input' } %> - <% else %> - <%= ff.input :field_type, as: :string, disabled: true, input_html: { class: "form-item--input" } %> - <% end %> -
    diff --git a/app/views/binda/field_groups/form_section/_form_section_header.html.erb b/app/views/binda/field_groups/form_section/_form_section_header.html.erb new file mode 100644 index 00000000..b9dedd29 --- /dev/null +++ b/app/views/binda/field_groups/form_section/_form_section_header.html.erb @@ -0,0 +1,44 @@ +
    + + + + <%= t'binda.done' %> + + + + <%= t'binda.sort_items' %> + + + <% if defined? repeater %> + + + <%= t(:new_item_in_repeater, arg1: repeater.name.capitalize) %> + <%= t('binda.field_setting.singular') %> + + <% else %> + + + <%= t('binda.field_setting.new') %> + + <% end %> +

    + <%= f.object.name %> +

    + <% unless f.object.description.blank? %> +

    + <%= f.object.description %> +

    + <% end %> +
    \ No newline at end of file diff --git a/app/views/binda/field_groups/form_section/_form_section_list.html.erb b/app/views/binda/field_groups/form_section/_form_section_list.html.erb new file mode 100644 index 00000000..4752a3c9 --- /dev/null +++ b/app/views/binda/field_groups/form_section/_form_section_list.html.erb @@ -0,0 +1,12 @@ +
      + <% ancestry = defined?(repeater) ? repeater.id.to_s : nil %> + <%= f.simple_fields_for :field_settings, f.object.field_settings.where( ancestry: ancestry ).order( :position, :name ) do |ff| %> +
    • form-item--collapsable form-item--collapsed"> + <%= render 'form_item', f: f, ff: ff, isNew: false %> +
    • + <% end %> +
    \ No newline at end of file diff --git a/app/views/binda/fieldable/_form_item_new_repeater.html.erb b/app/views/binda/fieldable/_form_item_new_repeater.html.erb index c40e3b3e..5a1c4c94 100644 --- a/app/views/binda/fieldable/_form_item_new_repeater.html.erb +++ b/app/views/binda/fieldable/_form_item_new_repeater.html.erb @@ -1,25 +1,8 @@ <%= simple_form_for [@instance.structure, @instance], defaults: { label: false }, html: { multipart: true } do |f| %> -
  • -
    - - <%= t('binda.collapse') %> - <%= t('binda.expand') %> - - <%= t('binda.delete') %> -

    <%= @repeater.field_setting.name %> <%= t('binda.item') %> #<%= @repeater.id %>

    -
    -
    - <%= f.simple_fields_for "repeaters_attributes[]", @repeater do |ff| %> - <%= ff.input :fieldable_id, as: :hidden %> - <%= ff.input :fieldable_type, as: :hidden %> - <%= ff.input :field_setting_id, as: :hidden %> - <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> - <% ff.object.field_setting.children.order( :position, :id ).each do |repeater_setting_child| %> - <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child %> - <% end %> - <% end %> -
    -
  • + <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_list_item', + repeater_setting: @repeater.field_setting, + repeater: @repeater, + f: f %> <% end %> \ No newline at end of file diff --git a/app/views/binda/fieldable/_form_section.html.erb b/app/views/binda/fieldable/_form_section.html.erb index c2ae4af9..6392266d 100644 --- a/app/views/binda/fieldable/_form_section.html.erb +++ b/app/views/binda/fieldable/_form_section.html.erb @@ -48,14 +48,6 @@ <% end %> -<%# - - - - - - - - - - - - %> -<%# REPEATER %> -<%# - - - - - - - - - - - - %> -<% elsif field_setting.field_type == 'repeater' %> - <% repeaters = @instance.repeaters.where( field_setting: field_setting ).order('position DESC') %> - <%= render 'binda/fieldable/form_item_repeater', f: f, repeater_setting: field_setting, repeaters: repeaters %> - - <%# - - - - - - - - - - - - %> <%# RELATED_FIELD %> <%# - - - - - - - - - - - - %> @@ -71,4 +63,13 @@ <% elsif ['radio', 'selection', 'checkbox'].include? field_setting.field_type %> <%= render 'binda/fieldable/form_item_selections', f: f, field_setting: field_setting %> + +<%# - - - - - - - - - - - - %> +<%# REPEATER %> +<%# - - - - - - - - - - - - %> +<% elsif field_setting.field_type == 'repeater' %> + <% repeaters = @instance.repeaters.where( field_setting: field_setting ).order('position ASC') %> + <%= render 'binda/fieldable/form_item_repeater', f: f, repeater_setting: field_setting, repeaters: repeaters %> + + <% end %> diff --git a/app/views/binda/fieldable/_form_section_repeater.html.erb b/app/views/binda/fieldable/_form_section_repeater.html.erb index 1065cf42..5723ecba 100644 --- a/app/views/binda/fieldable/_form_section_repeater.html.erb +++ b/app/views/binda/fieldable/_form_section_repeater.html.erb @@ -48,7 +48,6 @@ <% end %> - <%# - - - - - - - - - - - - %> <%# RELATED_FIELD %> <%# - - - - - - - - - - - - %> diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb index 0b4139ff..5e84eca6 100644 --- a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb index 28006e3b..604b7c06 100644 --- a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb @@ -1,16 +1,18 @@
      + data-message="<%= t 'binda.sort_items_loader', { arg1: t('binda.field_setting.plural').downcase } %>"> <% if repeaters.size == 0 %> - <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_empty_list', - repeaters: repeaters, + <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_list_item', + repeater: @instance.find_or_create_a_field_by( repeater_setting.id, 'repeater' ), repeater_setting: repeater_setting, f: f %> <% else %> - <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_default_list', - repeaters: repeaters, - repeater_setting: repeater_setting, - f: f %> + <% repeaters.each do |repeater| %> + <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_list_item', + repeater: repeater, + repeater_setting: repeater_setting, + f: f %> + <% end %> <% end %>
    \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list_item.html.erb similarity index 71% rename from app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb rename to app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list_item.html.erb index ceddbb8d..c957654a 100644 --- a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_empty_list.html.erb +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list_item.html.erb @@ -1,4 +1,3 @@ -<% repeater = @instance.find_or_create_a_field_by( repeater_setting.id, 'repeater' ) %>
  • -
    +
    <%= f.simple_fields_for "repeaters_attributes[]", repeater do |ff| %> + <%= ff.input :field_setting_id, as: :hidden, input_html: { value: ff.object.field_setting.id } %> + <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> <%= ff.input :fieldable_id, as: :hidden %> <%= ff.input :fieldable_type, as: :hidden %> - <%= ff.input :field_setting_id, as: :hidden, input_html: { value: repeater_setting.id } %> - <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> - <% repeater_setting.children.order( :position, :id ).each do |repeater_setting_child| %> - <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child %> + <%# DON'T USE ff.input :position otherwise it will override the order set by rails via ajax when adding a new item %> + <% ff.object.field_setting.children.order( :position, :id ).each do |repeater_setting_child| %> + <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child, repeater: repeater %> <% end %> - <% end %> + <% end %>
  • \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb deleted file mode 100644 index a09fdf06..00000000 --- a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_populated_list.html.erb +++ /dev/null @@ -1,40 +0,0 @@ -<% repeaters.each do |repeater| %> -
  • -
    - - - - <%= t('binda.collapse') %> - - - - <%= t('binda.expand') %> - - - - - <%= t('binda.delete').capitalize %> - -

    - <%= repeater.field_setting.name %> - <%= t('binda.item') %> - #<%= repeater.id %> -

    -
    -
    - <%= f.simple_fields_for "repeaters_attributes[]", repeater do |ff| %> - <%= ff.input :field_setting_id, as: :hidden, input_html: { value: ff.object.field_setting.id } %> - <%= ff.input :id, as: :hidden, input_html: { value: ff.object.id } %> - <%= ff.input :fieldable_id, as: :hidden %> - <%= ff.input :fieldable_type, as: :hidden %> - <% ff.object.field_setting.children.order( :position, :id ).each do |repeater_setting_child| %> - <%= render "binda/fieldable/form_section_repeater", f: ff, repeater_setting_child: repeater_setting_child, repeater: repeater %> - <% end %> - <% end %> -
    -
  • -<% end %> \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index bf296af5..5e371f3e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -83,6 +83,7 @@ en: error_on_create: General Details group hasn't been created repeater_field_settings: Fields settings of the %{arg1} repeater field_setting: + new: New field setting singular: Field setting plural: Field settings confirm_delete: This operation will delete this setting and all related component content. Are you ok with that? diff --git a/config/routes.rb b/config/routes.rb index 4a1ba63e..f75b0f47 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -43,6 +43,8 @@ resources :field_groups do post 'field_settings/add_child' post 'field_settings/sort' + post 'sort_field_settings' + post 'new_field_setting' resources :field_settings end resources :boards do diff --git a/lib/generators/binda/install/install_generator.rb b/lib/generators/binda/install/install_generator.rb index 3109a2c6..a06d43b0 100644 --- a/lib/generators/binda/install/install_generator.rb +++ b/lib/generators/binda/install/install_generator.rb @@ -48,7 +48,6 @@ def run_migrations end end rake 'db:migrate' - end def add_route diff --git a/spec/features/components_edit_spec.rb b/spec/features/components_edit_spec.rb index a4ee0c7d..1ee6907b 100644 --- a/spec/features/components_edit_spec.rb +++ b/spec/features/components_edit_spec.rb @@ -69,6 +69,12 @@ skip "not implemeted yet" end + describe "when creating a new repeater" do + it "sets the new item as the first one (set its position as well)" do + skip "not implemeted yet" + end + end + it "allows to edit a text field" do skip "not implemeted yet" end diff --git a/spec/features/field_groups_edit__editor_spec.rb b/spec/features/field_groups_edit__editor_spec.rb index bddf5cad..cd6e3f81 100644 --- a/spec/features/field_groups_edit__editor_spec.rb +++ b/spec/features/field_groups_edit__editor_spec.rb @@ -29,18 +29,16 @@ find(add_new__button).click - # Upon clicking the 'add_new__button' link, a script clones and - # changes the id of the new form item adding a suffix based on the number of clicks. - click_counter = 1 - - # Make sure the form item appeared - sleep 1 + wait_for_ajax + sleep 1.2 + + new_field_setting = field_group.field_settings.order('id ASC').last - field_name_input = "field_group_new_field_settings__name-#{click_counter}" - field_name_value = "#{field_class.downcase.underscore}-test-#{click_counter}" - field_type_input = "field_group_new_field_settings__field_type-#{click_counter}" + field_name_input = "field_group_field_settings_attributes_#{new_field_setting.id}_name" + field_name_value = "Test #{new_field_setting.id}" + field_type_input = "field_group_field_settings_attributes_#{new_field_setting.id}_field_type" - within "#new-form-item-#{click_counter}" do + within "#form-item-#{new_field_setting.id}" do fill_in field_name_input, with: field_name_value, visible: true end select2("#{field_class.downcase.underscore}", field_type_input) @@ -51,7 +49,7 @@ field_group.reload - within "#form-item-#{field_group.field_settings.first.id}" do + within "#form-item-#{new_field_setting.id}" do find('.form-item--collapse-btn').click # Make sure the form item appeared sleep 1 @@ -69,25 +67,24 @@ path_to_field_group = binda.edit_structure_field_group_path( structure_id: @structure.slug, id: field_group.slug ) visit path_to_field_group - wrapper = "#form-section--repeater-#{repeater.id}" + wrapper = "#form-item--field-list-#{repeater.id}" # create variable to be available throughout the example field_name_value = '' field_type_input = '' within wrapper do - find("#form-item--repeater-#{repeater.id}--add-new").click + add_new__button = "#form-item--repeater-#{repeater.id}--add-new" + find(add_new__button).click + wait_for_ajax + sleep 1.2 - # Upon clicking the 'add_new__button' link, a script clones and - # changes the id of the new form item adding a suffix based on the number of clicks. - click_counter = 1 - - # Make sure the form item appeared - sleep 1 + new_field_setting = field_group.field_settings.order('id ASC').last + + field_name_input = "field_group_field_settings_attributes_#{new_field_setting.id}_name" + field_name_value = "Test #{new_field_setting.id}" + field_type_input = "field_group_field_settings_attributes_#{new_field_setting.id}_field_type" - field_name_input = "field_group_new_field_settings__name-#{click_counter}" - field_name_value = "#{field_class.downcase}-test-#{click_counter}" - field_type_input = "field_group_new_field_settings__field_type-#{click_counter}" fill_in field_name_input, with: field_name_value end @@ -100,7 +97,7 @@ field_group.reload repeater.reload - within "#form-section--repeater-#{repeater.id}" do + within "#form-item--field-list-#{repeater.id}" do find('.form-item--collapse-btn').click # Make sure the form item appeared sleep 1 diff --git a/spec/lib/generators/binda/setup_generator_spec.rb b/spec/lib/generators/binda/setup_generator_spec.rb index c82b89af..d2f50b97 100644 --- a/spec/lib/generators/binda/setup_generator_spec.rb +++ b/spec/lib/generators/binda/setup_generator_spec.rb @@ -23,6 +23,7 @@ module Binda prepare_destination # @old_stdin = STDIN + # Mock user input by adding 4 newlines which answer the setup questions (see setup generator) STDIN = StringIO.new("\n\n\n\n") run_generator # STDIN = @old_stdin @@ -69,18 +70,18 @@ module Binda describe "executing from an existing application" do before(:all) do - ::DatabaseCleaner.strategy = :truncation - ::DatabaseCleaner.clean + # ::DatabaseCleaner.strategy = :truncation + # ::DatabaseCleaner.clean prepare_destination # @old_stdin = STDIN - STDIN = StringIO.new("mail@admin.com\npassword\nWebsite name\nWebsite description\n") + STDIN = StringIO.new("new@admin.com\npassword\nWebsite name\nWebsite description\n") run_generator # STDIN = @old_stdin end it "can create a default user" do - expect(Binda::User.where(email: 'mail@admin.com').present?).to be true + expect(Binda::User.where(email: 'new@admin.com').present?).to be true end end end From 8c38fcfdb90bf1fa12b182b5f29016a2e3f2fe21 Mon Sep 17 00:00:00 2001 From: Alessandro Barbieri Date: Sun, 11 Feb 2018 22:28:53 +0100 Subject: [PATCH 06/38] FIX sortable feature on field settings and repeaters --- app/assets/javascripts/binda/application.js | 3 + .../binda/components/field_setting_choices.js | 10 +- .../binda/components/fileupload.js | 2 +- ...{form_item.js => form_item_collapsable.js} | 196 +- .../binda/components/form_item_editor.js | 39 - .../binda/components/form_item_image.js | 2 +- .../binda/components/form_item_repeater.js | 117 - .../binda/components/login-shader.js | 2 +- .../binda/components/login_form.js | 2 +- .../javascripts/binda/components/sortable.js | 46 +- .../javascripts/binda/dist/binda.bundle.js | 768 +- app/assets/javascripts/binda/index.js | 26 +- .../binda/components/form_item.scss | 28 +- app/controllers/binda/boards_controller.rb | 30 +- .../binda/components_controller.rb | 4 +- .../binda/field_groups_controller.rb | 10 +- app/helpers/binda/field_groups_helper.rb | 5 +- .../binda/field_groups/_form_item.html.erb | 8 +- .../field_groups/_form_new_item.html.erb | 8 +- .../_form_section_repeater.html.erb | 2 +- .../form_item/_form_item_editor.html.erb | 5 +- .../form_item/_form_item_header.html.erb | 16 +- .../_form_section_header.html.erb | 16 +- .../form_section/_form_section_list.html.erb | 9 +- .../_form_item_new_repeater.html.erb | 8 +- .../_form_item_repeater_header.html.erb | 11 +- .../_form_item_repeater_list.html.erb | 7 +- .../_form_item_repeater_list_item.html.erb | 6 +- config/routes.rb | 2 +- .../binda/components_controller_spec.rb | 6 +- spec/factories/boards.rb | 12 +- spec/factories/structures.rb | 8 + spec/features/boards_edit_spec.rb | 195 + spec/features/components_edit_spec.rb | 3 +- .../field_groups_edit__editor_spec.rb | 67 +- vendor/assets/javascripts/lodash.js | 17097 ++++++++++++++++ 36 files changed, 17892 insertions(+), 884 deletions(-) rename app/assets/javascripts/binda/components/{form_item.js => form_item_collapsable.js} (50%) delete mode 100644 app/assets/javascripts/binda/components/form_item_editor.js delete mode 100644 app/assets/javascripts/binda/components/form_item_repeater.js create mode 100644 spec/features/boards_edit_spec.rb create mode 100644 vendor/assets/javascripts/lodash.js diff --git a/app/assets/javascripts/binda/application.js b/app/assets/javascripts/binda/application.js index 299e44dd..d94faf30 100644 --- a/app/assets/javascripts/binda/application.js +++ b/app/assets/javascripts/binda/application.js @@ -27,6 +27,9 @@ // Select2 //= require select2/select2.full.min +// Lodash +//= require lodash + // TweenLite // GSAP/CSSPlugin.min // GSAP/EasePack.min diff --git a/app/assets/javascripts/binda/components/field_setting_choices.js b/app/assets/javascripts/binda/components/field_setting_choices.js index f2ba24ad..aff3f26d 100644 --- a/app/assets/javascripts/binda/components/field_setting_choices.js +++ b/app/assets/javascripts/binda/components/field_setting_choices.js @@ -2,14 +2,14 @@ * FORM ITEM CHOICE */ -import { _FormItemEditor } from "./form_item_editor"; +import { resizeCollapsableStacks } from "./form_item_collapsable"; class FieldSettingChoices { constructor() { this.target = ".field-setting-choices--choice"; } - isSet() { + isPresent() { if ($(this.target).length > 0) { return true; } else { @@ -39,7 +39,7 @@ class FieldSettingChoices { .closest(".field-setting-choices--choice") .remove(); // Update form item editor size - _FormItemEditor.resize(); + resizeCollapsableStacks(); } ); } @@ -65,7 +65,7 @@ function addChoice(event) { // Append the clone right after choices.prepend(clone); // Update form item editor size - _FormItemEditor.resize(); + resizeCollapsableStacks(); } function deleteChoice(event) { @@ -81,7 +81,7 @@ function deleteChoice(event) { }).done(function() { choice.remove(); // Update form item editor size - _FormItemEditor.resize(); + resizeCollapsableStacks(); }).fail(function(data){ alert(data.responseJSON.errors); }); diff --git a/app/assets/javascripts/binda/components/fileupload.js b/app/assets/javascripts/binda/components/fileupload.js index 361dc8ae..ac180151 100644 --- a/app/assets/javascripts/binda/components/fileupload.js +++ b/app/assets/javascripts/binda/components/fileupload.js @@ -10,7 +10,7 @@ class FileUpload { this.target = ".fileupload"; } - isSet() { + isPresent() { if ($(this.target).length > 0) { return true; } else { diff --git a/app/assets/javascripts/binda/components/form_item.js b/app/assets/javascripts/binda/components/form_item_collapsable.js similarity index 50% rename from app/assets/javascripts/binda/components/form_item.js rename to app/assets/javascripts/binda/components/form_item_collapsable.js index dfdad548..b61f478e 100644 --- a/app/assets/javascripts/binda/components/form_item.js +++ b/app/assets/javascripts/binda/components/form_item_collapsable.js @@ -2,17 +2,16 @@ * FORM ITEM */ -import { _FormItemEditor } from "./form_item_editor"; import { setupSelect2 } from "./select2"; // Component Global Variables let newFormItemId = 1; -class FormItem { +class FormItemCollapsable { constructor() {} - isSet() { - if ($(".form-item--add-new").length > 0) { + isPresent() { + if ($(".form--list").length > 0) { return true; } else { return false; @@ -20,24 +19,32 @@ class FormItem { } setEvents() { - $(document).on("click", ".form-item--add-new", addNewItem); - $(document).on("click", ".form-item--delete-item", deleteItem); + $(document).on("click", ".form--add-list-item", addNewItem); + $(document).on("click", ".form--delete-list-item", deleteItem); $(document).on("click", ".form-item--collapse-btn", collapseToggle); + $(window).resize(resizeCollapsableStacks); + // Make sure all collapsable items are resized already at every page load + resizeCollapsableStacks(); } } -export let _FormItem = new FormItem(); +export let _FormItemCollapsable = new FormItemCollapsable(); -///- - - - - - - - - - - - - - - - - - - - -/// COMPONENT HELPER FUNCTIONS -///- - - - - - - - - - - - - - - - - - - - +/** + * COMPONENT HELPER FUNCTIONS + */ +/** + * Adds a new item. + * + * @param {event} event The event + */ function addNewItem(event) { // Stop default behaviour event.preventDefault(); // Get the child to clone let id = $(this).data("id"); - let $list = $("#form-item--field-list-" + id); + let $list = $("#form--list-" + id); let url = $(this).data("url"); let data = $list.sortable("serialize"); let params = $(this).data("params"); @@ -56,13 +63,19 @@ function addNewItem(event) { }); } +/** + * Setup and append new item + * + * @param {string} newItem The new item + * @param {object} $list The list + */ function setupAndAppend(newItem, $list) { // Append the item $list.prepend(newItem); let collapsable = $list.find(".form-item--collapsable").get(0); - // Prepare collapsable animation - collapsable.style.maxHeight = "0px"; + // Update select input for Select2 plugin + setupSelect2($list.find("select")); // Setup TinyMCE for the newly created item var textarea_editor_id = $list @@ -71,21 +84,11 @@ function setupAndAppend(newItem, $list) { .attr("id"); tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); - // Prepare collapsable stack animation - $(collapsable) - .find(".form-item--collapsable-stack") - .each(function() { - if (!$list.hasClass("sortable--enabled")) { - // Prepare field stack for collapsable animation - this.style.maxHeight = collapsable.scrollHeight + "px"; - } - }); - - // Resize the editor (is it needed with the new configuration?) - // _FormItemEditor.resize() + // Prepare collapsable animation + collapsable.style.maxHeight = "0px"; - // Update select input for Select2 plugin - setupSelect2($list.find("select")); + // Prepare collapsable stack animation + openCollapsableStacks(collapsable); // Refresh Sortable to update the added item with Sortable features $list.sortable("refresh"); @@ -96,83 +99,76 @@ function setupAndAppend(newItem, $list) { }, 50); } -// This function could be improved as it generates an issue with -// input ids which are duplicated after the entire target has been cloned -function DEPRECATEDaddNewItem(event) { - // Stop default behaviour - event.preventDefault(); - // Get the child to clone - // (`this` always refers to the second argument of the $(document).on() method, in this case '.form-item--add-new') - let id = $(this).data("new-form-item-id"); - let $newChild = $("#" + id); - // Clone child and remove id and styles from cloned child - $newChild.clone().insertAfter($newChild); - // Remove class in order to remove styles, and change id so it's reachable when testing - $newChild - .removeClass("form-item--new") - .attr("id", "new-form-item-" + newFormItemId); - - // // Update all ids to avoid duplication - $newChild.find("[id]").each(function() { - let oldId = $(this).attr("id"); - let newId = oldId + "-" + newFormItemId; - $(this).attr("id", newId); - let $forId = $newChild.find("[for=" + oldId + "]"); - if ($forId.length > 0) { - $forId.attr("for", newId); - } - }); - - // Update height (max-height) of the new element - let $formItemEditor = $("#new-form-item-" + newFormItemId).find( - ".form-item--editor" - ); - - // override current max-height which is set to 0 - $formItemEditor.get(0).style.maxHeight = - $formItemEditor.get(0).scrollHeight + "px"; - - _FormItemEditor.resize(); - - // Increment global id variable `newFormItemId` in case needs to be used again - newFormItemId++; - - setupSelect2($formItemEditor.find("select")); -} - -function close() { - this.style.maxHeight = "0px"; +/** + * Close collapsable stacks + * + * It closes all collapsable stacks inside the collapsable item passed as argument + * + * @param {object, string} target The target + */ +export function closeCollapsableStacks(obj) { + $(obj) + .addClass("form-item--collapsed") + .find(".form-item--collapsable-stack") + .each(function() { + this.style.maxHeight = "0px"; + }); } -function open() { - this.style.maxHeight = this.scrollHeight + "px"; +/** + * Open collapsable stacks + * + * It opens all collapsable stacks inside the collapsable item passed as argument + * + * @param {object, string} target The target + */ +export function openCollapsableStacks(obj) { + // Don't execute this if sortable is enabled + if ( + $(obj) + .closest(".sortable") + .hasClass("sortable--enabled") + ) { + return; + } + $(obj) + .removeClass("form-item--collapsed") + .find(".form-item--collapsable-stack") + .each(function() { + this.style.maxHeight = this.scrollHeight + "px"; + }); } +/** + * Toggle a collapsable item + * + * Basically it opens it or closes it based on its state + * + * @param {event} event The event + */ function collapseToggle(event) { - // This function is temporarely just set for repeaters. - // TODO: Need refactoring in order to be available also for generic form items - // Stop default behaviour event.preventDefault(); let $collapsable = $(this).closest(".form-item--collapsable"); if ($collapsable.hasClass("form-item--collapsed")) { - $collapsable.find(".form-item--collapsable-stack").each(open); - $collapsable.find(".form-item--editor").each(open); - $collapsable.removeClass("form-item--collapsed"); + openCollapsableStacks($collapsable); } else { - $collapsable.find(".form-item--collapsable-stack").each(close); - $collapsable.find(".form-item--editor").each(close); - $collapsable.addClass("form-item--collapsed"); + closeCollapsableStacks($collapsable); } } +/** + * Delete collapsable item and hide it + * + * @param {event} event The event + */ function deleteItem(event) { // Stop default behaviour event.preventDefault(); let record_id = $(this).data("id"); - let targetId = `#form-item-${record_id}` + let targetId = `#form--list-item-${record_id}`; let target = $(targetId).get(0); // As max-height isn't set you need to set it manually before changing it, // otherwise the animation doesn't get triggered @@ -194,3 +190,33 @@ function deleteItem(event) { }); // TODO add a fallback if request fails } + +/** + */ + +/** + * Resize all collapsable item + * + * If a target is passed as a argument the function will resize only that target and its children. + * + * @param {object, string} target The target + */ +export function resizeCollapsableStacks(target) { + target = _.isUndefined(target) ? $(".form-item--collapsable-stack") : target; + $(target).each(function() { + // If the collapsable item is closed don't go any further + if ( + $(this).height() === 0 || + $(this) + .closest(".form-item--collapsable") + .hasClass("form-item--collapsed") + ) { + this.style.maxHeight = "0px"; + } else { + // otherwise update the max-height which is needed for the CSS transition + // NOTE you need to remove the max-height (inside 'style' attribute) to get the real height + this.style.height = "auto"; + this.style.maxHeight = this.scrollHeight + "px"; + } + }); +} diff --git a/app/assets/javascripts/binda/components/form_item_editor.js b/app/assets/javascripts/binda/components/form_item_editor.js deleted file mode 100644 index 104f45b9..00000000 --- a/app/assets/javascripts/binda/components/form_item_editor.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * FORM ITEM EDITOR - */ - -class FormItemEditor { - constructor() { - this.target = ".form-item--editor"; - } - - isSet() { - if ($(this.target).length > 0) { - return true; - } else { - return false; - } - } - - setEvents() { - // run resize to set initial size - this.resize(); - // run resize on each of these events - $(window).resize(() => { - this.resize(); - }); - } - - resize() { - $(this.target).each(function() { - // If the form item editor is closed don't go any further - if ($(this).height() === 0) return; - // otherwise update the max-height which is needed for the CSS transition - // NOTE you need to remove the max-height (inside 'style' attribute) to get the real height - $(this).get(0).style.height = "auto"; - $(this).get(0).style.maxHeight = $(this).get(0).scrollHeight + "px"; - }); - } -} - -export let _FormItemEditor = new FormItemEditor(); diff --git a/app/assets/javascripts/binda/components/form_item_image.js b/app/assets/javascripts/binda/components/form_item_image.js index df3aef10..8dd11797 100644 --- a/app/assets/javascripts/binda/components/form_item_image.js +++ b/app/assets/javascripts/binda/components/form_item_image.js @@ -7,7 +7,7 @@ class FormItemImage { this.target = ".form-item--image--uploader"; } - isSet() { + isPresent() { if ($(this.target).length > 0) { return true; } else { diff --git a/app/assets/javascripts/binda/components/form_item_repeater.js b/app/assets/javascripts/binda/components/form_item_repeater.js deleted file mode 100644 index 5d3994d6..00000000 --- a/app/assets/javascripts/binda/components/form_item_repeater.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * FORM ITEM REPEATER - */ - -import { _FormItemEditor } from "./form_item_editor"; -import { setupSelect2 } from "./select2"; - -class FormItemRepeater { - constructor() {} - - isSet() { - if ($(".form-item--repeater-section").length > 0) { - return true; - } else { - return false; - } - } - - setEvents() { - $(document).on("click", ".form-item--repeater-section--add-new", addNewItem); - $(document).on("click", ".form-item--delete-repeater-item", deleteRepeter); - } -} - -export let _FormItemRepeater = new FormItemRepeater(); - -/** - * COMPONENT HELPER FUNCTIONS - */ - -function addNewItem(event) { - // Stop default behaviour - event.preventDefault(); - // Get the child to clone - let id = $(this).data("id"); - let $list = $("#form-item--repeater-setting-" + id); - let url = $(this).data("url"); - let data = $list.sortable("serialize"); - data = data.concat(`&repeater_setting_id=${id}`); - - $.post(url, data, function(data) { - // Get repaeter code from Rails - // Due to the Rails way of creating nested forms it's necessary to - // create the nested item inside a different new form, then get just - // the code contained between the two SPLIT comments - let parts = data.split(""); - let newRepeater = parts[1]; - setupAndAppend(newRepeater, $list); - }); -} - -function setupAndAppend(newRepeater, $list) { - // Append the item - $list.prepend(newRepeater); - let new_repeater_item = $list.find(".form-item--repeater").get(0); - - // Prepare repeater animation - new_repeater_item.style.maxHeight = "0px"; - - // Setup TinyMCE for the newly created item - var textarea_editor_id = $list - .find("textarea") - .last("textarea") - .attr("id"); - tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); - - // // Prepare collapsable stack animation - // $(new_repeater_item) - // .find(".form-item--collapsable-stack") - // .each(function() { - // if (!$list.hasClass("sortable--enabled")) { - // // Prepare field stack for collapsable animation - // this.style.maxHeight = new_repeater_item.scrollHeight + "px"; - // } - // }); - - // Resize the editor (is it needed with the new configuration?) - // _FormItemEditor.resize() - - // Update select input for Select2 plugin - setupSelect2($list.find("select")); - - // Refresh Sortable to update the added item with Sortable features - $list.sortable("refresh"); - - // Run animation 50ms after previous style declaration (see above) otherwise animation doesn't get triggered - setTimeout(function() { - new_repeater_item.style.maxHeight = new_repeater_item.scrollHeight + "px"; - }, 50); -} - -function deleteRepeter(event) { - // Stop default behaviour - event.preventDefault(); - - let record_id = $(this).data("id"); - let targetId = `#repeater_${record_id}` - let target = $(targetId).get(0); - // As max-height isn't set you need to set it manually before changing it, - // otherwise the animation doesn't get triggered - target.style.maxHeight = target.scrollHeight + "px"; - // Change max-height after 50ms to trigger css animation - setTimeout(function() { - target.style.maxHeight = 0 + "px"; - }, 50); - - $.ajax({ - url: $(this).attr("href"), - data: { id: record_id, target_id: targetId, isAjax: true }, - method: "DELETE" - }).done(data => { - // Make sure the animation completes before removing the item (it should last 600ms + 50ms) - setTimeout(function() { - $(data.target_id).remove(); - }, 700); - }); -} diff --git a/app/assets/javascripts/binda/components/login-shader.js b/app/assets/javascripts/binda/components/login-shader.js index 88abacc8..efd4bda9 100644 --- a/app/assets/javascripts/binda/components/login-shader.js +++ b/app/assets/javascripts/binda/components/login-shader.js @@ -14,7 +14,7 @@ class Shader { }; } - isSet() { + isPresent() { if ($("#background-shader").length > 0) { return true; } else { diff --git a/app/assets/javascripts/binda/components/login_form.js b/app/assets/javascripts/binda/components/login_form.js index 073641c9..87da0cbb 100644 --- a/app/assets/javascripts/binda/components/login_form.js +++ b/app/assets/javascripts/binda/components/login_form.js @@ -11,7 +11,7 @@ class LoginForm { this.isFilled = false; } - isSet() { + isPresent() { if ($(".login--form").length > 0) { return true; } else { diff --git a/app/assets/javascripts/binda/components/sortable.js b/app/assets/javascripts/binda/components/sortable.js index cf45e51c..736428fd 100644 --- a/app/assets/javascripts/binda/components/sortable.js +++ b/app/assets/javascripts/binda/components/sortable.js @@ -2,6 +2,8 @@ * SORTABLE */ +import { openCollapsableStacks, closeCollapsableStacks } from './form_item_collapsable'; + var sortableOptions = { stop: function(event, ui) { ui.item.css("z-index", 0); @@ -10,6 +12,14 @@ var sortableOptions = { update: updateSortable }; +/* Initialize jQuery Sortable + * + * This function handles several things: + * - it sets Sortable for each ".sortable" element + * - it adds handles only if required + * - it disable itself if it finds ".sortable--disabled" class + * - it sets up a toggle button and behaviour if required + */ export default function() { if ($(".sortable").length > 0) { // Initialize sortable item @@ -31,33 +41,19 @@ export default function() { if ($(".sortable--toggle").length > 0) { setupSortableToggle(); } - - // Add event to any sortable toggle button - $(document).on( - "click", - ".standard-form--repeater .sortable--toggle, .standard-form--field-group .sortable--toggle", - toggleSortable - ); } +/* Setup Sortable Toggle + * + * It sets up each toggle button and add the events needed to enable or disable Sortable. + */ function setupSortableToggle() { $(".sortable--toggle").each(function() { let id = "#" + $(this).data("sortable-target-id"); - $(id) - .find(".form-item--collapsable") - .addClass("form-item--collapsed"); - $(id) - .find(".form-item--collapsable-stack") - .each(close); + closeCollapsableStacks(id); }); -} - -function close() { - this.style.maxHeight = "0px"; -} - -function open() { - this.style.maxHeight = this.scrollHeight + "px"; + // Add event to any sortable toggle button + $(document).on("click", ".sortable--toggle", toggleSortable); } function toggleSortable(event) { @@ -66,14 +62,10 @@ function toggleSortable(event) { if ($(id).hasClass("sortable--disabled")) { $(id).sortable("enable"); - $(id) - .find(".form-item--collapsable-stack") - .each(close); - $(id) - .find(".form-item--collapsable") - .addClass("form-item--collapsed"); + closeCollapsableStacks(id); } else { $(id).sortable("disable"); + openCollapsableStacks(id); } $(id).toggleClass("sortable--disabled"); diff --git a/app/assets/javascripts/binda/dist/binda.bundle.js b/app/assets/javascripts/binda/dist/binda.bundle.js index 2ae0d4f3..8350c362 100644 --- a/app/assets/javascripts/binda/dist/binda.bundle.js +++ b/app/assets/javascripts/binda/dist/binda.bundle.js @@ -64,67 +64,7 @@ /******/ }) /************************************************************************/ /******/ ([ -/* 0 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _FormItemEditor; }); -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * FORM ITEM EDITOR - */ - -var FormItemEditor = function () { - function FormItemEditor() { - _classCallCheck(this, FormItemEditor); - - this.target = ".form-item--editor"; - } - - _createClass(FormItemEditor, [{ - key: "isSet", - value: function isSet() { - if ($(this.target).length > 0) { - return true; - } else { - return false; - } - } - }, { - key: "setEvents", - value: function setEvents() { - var _this = this; - - // run resize to set initial size - this.resize(); - // run resize on each of these events - $(window).resize(function () { - _this.resize(); - }); - } - }, { - key: "resize", - value: function resize() { - $(this.target).each(function () { - // If the form item editor is closed don't go any further - if ($(this).height() === 0) return; - // otherwise update the max-height which is needed for the CSS transition - // NOTE you need to remove the max-height (inside 'style' attribute) to get the real height - $(this).get(0).style.height = "auto"; - $(this).get(0).style.maxHeight = $(this).get(0).scrollHeight + "px"; - }); - } - }]); - - return FormItemEditor; -}(); - -var _FormItemEditor = new FormItemEditor(); - -/***/ }), +/* 0 */, /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -164,19 +104,17 @@ function setupSelect2(target) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_form_item__ = __webpack_require__(3); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_form_item_repeater__ = __webpack_require__(4); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__components_form_item_image__ = __webpack_require__(5); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__components_field_setting_choices__ = __webpack_require__(6); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__components_form_item_editor__ = __webpack_require__(0); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__components_fileupload__ = __webpack_require__(7); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__components_login_shader__ = __webpack_require__(8); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__components_login_form__ = __webpack_require__(9); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__components_sortable__ = __webpack_require__(10); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__components_field_group_editor__ = __webpack_require__(11); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__components_bootstrap__ = __webpack_require__(12); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_11__components_select2__ = __webpack_require__(1); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_12__components_radio_toggle__ = __webpack_require__(13); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_form_item_collapsable__ = __webpack_require__(14); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_form_item_image__ = __webpack_require__(5); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__components_field_setting_choices__ = __webpack_require__(6); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__components_fileupload__ = __webpack_require__(7); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__components_login_shader__ = __webpack_require__(8); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__components_login_form__ = __webpack_require__(9); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__components_sortable__ = __webpack_require__(10); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__components_field_group_editor__ = __webpack_require__(11); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__components_bootstrap__ = __webpack_require__(12); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__components_select2__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__components_radio_toggle__ = __webpack_require__(13); ///- - - - - - - - - - - - - - - - - - - - /// INDEX OF BINDA'S SCRIPTS ///- - - - - - - - - - - - - - - - - - - - @@ -193,391 +131,43 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); - - $(document).ready(function () { - if (__WEBPACK_IMPORTED_MODULE_0__components_form_item__["a" /* _FormItem */].isSet()) { - __WEBPACK_IMPORTED_MODULE_0__components_form_item__["a" /* _FormItem */].setEvents(); - } - if (__WEBPACK_IMPORTED_MODULE_1__components_form_item_repeater__["a" /* _FormItemRepeater */].isSet()) { - __WEBPACK_IMPORTED_MODULE_1__components_form_item_repeater__["a" /* _FormItemRepeater */].setEvents(); + if (__WEBPACK_IMPORTED_MODULE_0__components_form_item_collapsable__["a" /* _FormItemCollapsable */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_0__components_form_item_collapsable__["a" /* _FormItemCollapsable */].setEvents(); } - if (__WEBPACK_IMPORTED_MODULE_2__components_form_item_image__["a" /* _FormItemImage */].isSet()) { - __WEBPACK_IMPORTED_MODULE_2__components_form_item_image__["a" /* _FormItemImage */].setEvents(); + if (__WEBPACK_IMPORTED_MODULE_1__components_form_item_image__["a" /* _FormItemImage */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_1__components_form_item_image__["a" /* _FormItemImage */].setEvents(); } - if (__WEBPACK_IMPORTED_MODULE_3__components_field_setting_choices__["a" /* _FieldSettingChoices */].isSet()) { - __WEBPACK_IMPORTED_MODULE_3__components_field_setting_choices__["a" /* _FieldSettingChoices */].setEvents(); + if (__WEBPACK_IMPORTED_MODULE_2__components_field_setting_choices__["a" /* _FieldSettingChoices */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_2__components_field_setting_choices__["a" /* _FieldSettingChoices */].setEvents(); } - if (__WEBPACK_IMPORTED_MODULE_4__components_form_item_editor__["a" /* _FormItemEditor */].isSet()) { - __WEBPACK_IMPORTED_MODULE_4__components_form_item_editor__["a" /* _FormItemEditor */].setEvents(); + if (__WEBPACK_IMPORTED_MODULE_3__components_fileupload__["a" /* _FileUpload */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_3__components_fileupload__["a" /* _FileUpload */].setEvents(); } - if (__WEBPACK_IMPORTED_MODULE_5__components_fileupload__["a" /* _FileUpload */].isSet()) { - __WEBPACK_IMPORTED_MODULE_5__components_fileupload__["a" /* _FileUpload */].setEvents(); + if (__WEBPACK_IMPORTED_MODULE_5__components_login_form__["a" /* _LoginForm */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_5__components_login_form__["a" /* _LoginForm */].init(); } - if (__WEBPACK_IMPORTED_MODULE_7__components_login_form__["a" /* _LoginForm */].isSet()) { - __WEBPACK_IMPORTED_MODULE_7__components_login_form__["a" /* _LoginForm */].init(); + if (__WEBPACK_IMPORTED_MODULE_4__components_login_shader__["a" /* _Shader */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_4__components_login_shader__["a" /* _Shader */].setup(); + __WEBPACK_IMPORTED_MODULE_4__components_login_shader__["a" /* _Shader */].start(); } - if (__WEBPACK_IMPORTED_MODULE_6__components_login_shader__["a" /* _Shader */].isSet()) { - __WEBPACK_IMPORTED_MODULE_6__components_login_shader__["a" /* _Shader */].setup(); - __WEBPACK_IMPORTED_MODULE_6__components_login_shader__["a" /* _Shader */].start(); - } - Object(__WEBPACK_IMPORTED_MODULE_12__components_radio_toggle__["a" /* default */])(); - Object(__WEBPACK_IMPORTED_MODULE_8__components_sortable__["a" /* default */])(); - Object(__WEBPACK_IMPORTED_MODULE_9__components_field_group_editor__["a" /* default */])(); - Object(__WEBPACK_IMPORTED_MODULE_10__components_bootstrap__["a" /* default */])(); - Object(__WEBPACK_IMPORTED_MODULE_11__components_select2__["a" /* default */])(); + Object(__WEBPACK_IMPORTED_MODULE_10__components_radio_toggle__["a" /* default */])(); + Object(__WEBPACK_IMPORTED_MODULE_6__components_sortable__["a" /* default */])(); + Object(__WEBPACK_IMPORTED_MODULE_7__components_field_group_editor__["a" /* default */])(); + Object(__WEBPACK_IMPORTED_MODULE_8__components_bootstrap__["a" /* default */])(); + Object(__WEBPACK_IMPORTED_MODULE_9__components_select2__["a" /* default */])(); }); // handle event window.addEventListener("optimizedResize", function () { - if (__WEBPACK_IMPORTED_MODULE_6__components_login_shader__["a" /* _Shader */].isSet()) { - __WEBPACK_IMPORTED_MODULE_6__components_login_shader__["a" /* _Shader */].resize(); + if (__WEBPACK_IMPORTED_MODULE_4__components_login_shader__["a" /* _Shader */].isPresent()) { + __WEBPACK_IMPORTED_MODULE_4__components_login_shader__["a" /* _Shader */].resize(); } }); /***/ }), -/* 3 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _FormItem; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__form_item_editor__ = __webpack_require__(0); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__select2__ = __webpack_require__(1); -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * FORM ITEM - */ - - - - -// Component Global Variables -var newFormItemId = 1; - -var FormItem = function () { - function FormItem() { - _classCallCheck(this, FormItem); - } - - _createClass(FormItem, [{ - key: "isSet", - value: function isSet() { - if ($(".form-item--add-new").length > 0) { - return true; - } else { - return false; - } - } - }, { - key: "setEvents", - value: function setEvents() { - $(document).on("click", ".form-item--add-new", addNewItem); - $(document).on("click", ".form-item--delete-item", deleteItem); - $(document).on("click", ".form-item--collapse-btn", collapseToggle); - } - }]); - - return FormItem; -}(); - -var _FormItem = new FormItem(); - -///- - - - - - - - - - - - - - - - - - - - -/// COMPONENT HELPER FUNCTIONS -///- - - - - - - - - - - - - - - - - - - - - -function addNewItem(event) { - // Stop default behaviour - event.preventDefault(); - // Get the child to clone - var id = $(this).data("id"); - var $list = $("#form-item--field-list-" + id); - var url = $(this).data("url"); - var data = $list.sortable("serialize"); - var params = $(this).data("params"); - if (params) { - data = data.concat("&" + params); - } - - $.post(url, data, function (data) { - // Get repaeter code from Rails - // Due to the Rails way of creating nested forms it's necessary to - // create the nested item inside a different new form, then get just - // the code contained between the two SPLIT comments - var parts = data.split(""); - var newItem = parts[1]; - setupAndAppend(newItem, $list); - }); -} - -function setupAndAppend(newItem, $list) { - // Append the item - $list.prepend(newItem); - var collapsable = $list.find(".form-item--collapsable").get(0); - - // Prepare collapsable animation - collapsable.style.maxHeight = "0px"; - - // Setup TinyMCE for the newly created item - var textarea_editor_id = $list.find("textarea").last("textarea").attr("id"); - tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); - - // Prepare collapsable stack animation - $(collapsable).find(".form-item--collapsable-stack").each(function () { - if (!$list.hasClass("sortable--enabled")) { - // Prepare field stack for collapsable animation - this.style.maxHeight = collapsable.scrollHeight + "px"; - } - }); - - // Resize the editor (is it needed with the new configuration?) - // _FormItemEditor.resize() - - // Update select input for Select2 plugin - Object(__WEBPACK_IMPORTED_MODULE_1__select2__["b" /* setupSelect2 */])($list.find("select")); - - // Refresh Sortable to update the added item with Sortable features - $list.sortable("refresh"); - - // Run animation 500ms after previous style declaration (see above) otherwise animation doesn't get triggered - setTimeout(function () { - collapsable.style.maxHeight = collapsable.scrollHeight + "px"; - }, 50); -} - -// This function could be improved as it generates an issue with -// input ids which are duplicated after the entire target has been cloned -function DEPRECATEDaddNewItem(event) { - // Stop default behaviour - event.preventDefault(); - // Get the child to clone - // (`this` always refers to the second argument of the $(document).on() method, in this case '.form-item--add-new') - var id = $(this).data("new-form-item-id"); - var $newChild = $("#" + id); - // Clone child and remove id and styles from cloned child - $newChild.clone().insertAfter($newChild); - // Remove class in order to remove styles, and change id so it's reachable when testing - $newChild.removeClass("form-item--new").attr("id", "new-form-item-" + newFormItemId); - - // // Update all ids to avoid duplication - $newChild.find("[id]").each(function () { - var oldId = $(this).attr("id"); - var newId = oldId + "-" + newFormItemId; - $(this).attr("id", newId); - var $forId = $newChild.find("[for=" + oldId + "]"); - if ($forId.length > 0) { - $forId.attr("for", newId); - } - }); - - // Update height (max-height) of the new element - var $formItemEditor = $("#new-form-item-" + newFormItemId).find(".form-item--editor"); - - // override current max-height which is set to 0 - $formItemEditor.get(0).style.maxHeight = $formItemEditor.get(0).scrollHeight + "px"; - - __WEBPACK_IMPORTED_MODULE_0__form_item_editor__["a" /* _FormItemEditor */].resize(); - - // Increment global id variable `newFormItemId` in case needs to be used again - newFormItemId++; - - Object(__WEBPACK_IMPORTED_MODULE_1__select2__["b" /* setupSelect2 */])($formItemEditor.find("select")); -} - -function close() { - this.style.maxHeight = "0px"; -} - -function open() { - this.style.maxHeight = this.scrollHeight + "px"; -} - -function collapseToggle(event) { - // This function is temporarely just set for repeaters. - // TODO: Need refactoring in order to be available also for generic form items - - // Stop default behaviour - event.preventDefault(); - - var $collapsable = $(this).closest(".form-item--collapsable"); - - if ($collapsable.hasClass("form-item--collapsed")) { - $collapsable.find(".form-item--collapsable-stack").each(open); - $collapsable.find(".form-item--editor").each(open); - $collapsable.removeClass("form-item--collapsed"); - } else { - $collapsable.find(".form-item--collapsable-stack").each(close); - $collapsable.find(".form-item--editor").each(close); - $collapsable.addClass("form-item--collapsed"); - } -} - -function deleteItem(event) { - // Stop default behaviour - event.preventDefault(); - var record_id = $(this).data("id"); - var targetId = "#form-item-" + record_id; - var target = $(targetId).get(0); - // As max-height isn't set you need to set it manually before changing it, - // otherwise the animation doesn't get triggered - target.style.maxHeight = target.scrollHeight + "px"; - // Change max-height after 50ms to trigger css animation - setTimeout(function () { - target.style.maxHeight = "0px"; - }, 50); - - $.ajax({ - url: $(this).attr("href"), - data: { id: record_id, target_id: targetId, isAjax: true }, - method: "DELETE" - }).done(function (data) { - // Make sure the animation completes before removing the item (it should last 600ms + 50ms) - setTimeout(function () { - $(data.target_id).remove(); - }, 700); - }); - // TODO add a fallback if request fails -} - -/***/ }), -/* 4 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _FormItemRepeater; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__form_item_editor__ = __webpack_require__(0); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__select2__ = __webpack_require__(1); -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * FORM ITEM REPEATER - */ - - - - -var FormItemRepeater = function () { - function FormItemRepeater() { - _classCallCheck(this, FormItemRepeater); - } - - _createClass(FormItemRepeater, [{ - key: "isSet", - value: function isSet() { - if ($(".form-item--repeater-section").length > 0) { - return true; - } else { - return false; - } - } - }, { - key: "setEvents", - value: function setEvents() { - $(document).on("click", ".form-item--repeater-section--add-new", addNewItem); - $(document).on("click", ".form-item--delete-repeater-item", deleteRepeter); - } - }]); - - return FormItemRepeater; -}(); - -var _FormItemRepeater = new FormItemRepeater(); - -/** - * COMPONENT HELPER FUNCTIONS - */ - -function addNewItem(event) { - // Stop default behaviour - event.preventDefault(); - // Get the child to clone - var id = $(this).data("id"); - var $list = $("#form-item--repeater-setting-" + id); - var url = $(this).data("url"); - var data = $list.sortable("serialize"); - data = data.concat("&repeater_setting_id=" + id); - - $.post(url, data, function (data) { - // Get repaeter code from Rails - // Due to the Rails way of creating nested forms it's necessary to - // create the nested item inside a different new form, then get just - // the code contained between the two SPLIT comments - var parts = data.split(""); - var newRepeater = parts[1]; - setupAndAppend(newRepeater, $list); - }); -} - -function setupAndAppend(newRepeater, $list) { - // Append the item - $list.prepend(newRepeater); - var new_repeater_item = $list.find(".form-item--repeater").get(0); - - // Prepare repeater animation - new_repeater_item.style.maxHeight = "0px"; - - // Setup TinyMCE for the newly created item - var textarea_editor_id = $list.find("textarea").last("textarea").attr("id"); - tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); - - // // Prepare collapsable stack animation - // $(new_repeater_item) - // .find(".form-item--collapsable-stack") - // .each(function() { - // if (!$list.hasClass("sortable--enabled")) { - // // Prepare field stack for collapsable animation - // this.style.maxHeight = new_repeater_item.scrollHeight + "px"; - // } - // }); - - // Resize the editor (is it needed with the new configuration?) - // _FormItemEditor.resize() - - // Update select input for Select2 plugin - Object(__WEBPACK_IMPORTED_MODULE_1__select2__["b" /* setupSelect2 */])($list.find("select")); - - // Refresh Sortable to update the added item with Sortable features - $list.sortable("refresh"); - - // Run animation 50ms after previous style declaration (see above) otherwise animation doesn't get triggered - setTimeout(function () { - new_repeater_item.style.maxHeight = new_repeater_item.scrollHeight + "px"; - }, 50); -} - -function deleteRepeter(event) { - // Stop default behaviour - event.preventDefault(); - - var record_id = $(this).data("id"); - var targetId = "#repeater_" + record_id; - var target = $(targetId).get(0); - // As max-height isn't set you need to set it manually before changing it, - // otherwise the animation doesn't get triggered - target.style.maxHeight = target.scrollHeight + "px"; - // Change max-height after 50ms to trigger css animation - setTimeout(function () { - target.style.maxHeight = 0 + "px"; - }, 50); - - $.ajax({ - url: $(this).attr("href"), - data: { id: record_id, target_id: targetId, isAjax: true }, - method: "DELETE" - }).done(function (data) { - // Make sure the animation completes before removing the item (it should last 600ms + 50ms) - setTimeout(function () { - $(data.target_id).remove(); - }, 700); - }); -} - -/***/ }), +/* 3 */, +/* 4 */, /* 5 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -599,8 +189,8 @@ var FormItemImage = function () { } _createClass(FormItemImage, [{ - key: "isSet", - value: function isSet() { + key: "isPresent", + value: function isPresent() { if ($(this.target).length > 0) { return true; } else { @@ -623,7 +213,7 @@ var _FormItemImage = new FormItemImage(); "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _FieldSettingChoices; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__form_item_editor__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__ = __webpack_require__(14); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -642,8 +232,8 @@ var FieldSettingChoices = function () { } _createClass(FieldSettingChoices, [{ - key: "isSet", - value: function isSet() { + key: "isPresent", + value: function isPresent() { if ($(this.target).length > 0) { return true; } else { @@ -661,7 +251,7 @@ var FieldSettingChoices = function () { event.preventDefault(); $(this).closest(".field-setting-choices--choice").remove(); // Update form item editor size - __WEBPACK_IMPORTED_MODULE_0__form_item_editor__["a" /* _FormItemEditor */].resize(); + Object(__WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__["d" /* resizeCollapsableStacks */])(); }); } }]); @@ -686,7 +276,7 @@ function addChoice(event) { // Append the clone right after choices.prepend(clone); // Update form item editor size - __WEBPACK_IMPORTED_MODULE_0__form_item_editor__["a" /* _FormItemEditor */].resize(); + Object(__WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__["d" /* resizeCollapsableStacks */])(); } function deleteChoice(event) { @@ -702,7 +292,7 @@ function deleteChoice(event) { }).done(function () { choice.remove(); // Update form item editor size - __WEBPACK_IMPORTED_MODULE_0__form_item_editor__["a" /* _FormItemEditor */].resize(); + Object(__WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__["d" /* resizeCollapsableStacks */])(); }).fail(function (data) { alert(data.responseJSON.errors); }); @@ -735,8 +325,8 @@ var FileUpload = function () { } _createClass(FileUpload, [{ - key: "isSet", - value: function isSet() { + key: "isPresent", + value: function isPresent() { if ($(this.target).length > 0) { return true; } else { @@ -946,8 +536,8 @@ var Shader = function () { } _createClass(Shader, [{ - key: "isSet", - value: function isSet() { + key: "isPresent", + value: function isPresent() { if ($("#background-shader").length > 0) { return true; } else { @@ -1160,8 +750,8 @@ var LoginForm = function () { } _createClass(LoginForm, [{ - key: "isSet", - value: function isSet() { + key: "isPresent", + value: function isPresent() { if ($(".login--form").length > 0) { return true; } else { @@ -1268,10 +858,13 @@ var _LoginForm = new LoginForm(); /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__ = __webpack_require__(14); /** * SORTABLE */ + + var sortableOptions = { stop: function stop(event, ui) { ui.item.css("z-index", 0); @@ -1280,6 +873,14 @@ var sortableOptions = { update: updateSortable }; +/* Initialize jQuery Sortable + * + * This function handles several things: + * - it sets Sortable for each ".sortable" element + * - it adds handles only if required + * - it disable itself if it finds ".sortable--disabled" class + * - it sets up a toggle button and behaviour if required + */ /* harmony default export */ __webpack_exports__["a"] = (function () { if ($(".sortable").length > 0) { // Initialize sortable item @@ -1301,25 +902,19 @@ var sortableOptions = { if ($(".sortable--toggle").length > 0) { setupSortableToggle(); } - - // Add event to any sortable toggle button - $(document).on("click", ".standard-form--repeater .sortable--toggle, .standard-form--field-group .sortable--toggle", toggleSortable); }); +/* Setup Sortable Toggle + * + * It sets up each toggle button and add the events needed to enable or disable Sortable. + */ function setupSortableToggle() { $(".sortable--toggle").each(function () { var id = "#" + $(this).data("sortable-target-id"); - $(id).find(".form-item--collapsable").addClass("form-item--collapsed"); - $(id).find(".form-item--collapsable-stack").each(close); + Object(__WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__["b" /* closeCollapsableStacks */])(id); }); -} - -function close() { - this.style.maxHeight = "0px"; -} - -function open() { - this.style.maxHeight = this.scrollHeight + "px"; + // Add event to any sortable toggle button + $(document).on("click", ".sortable--toggle", toggleSortable); } function toggleSortable(event) { @@ -1328,10 +923,10 @@ function toggleSortable(event) { if ($(id).hasClass("sortable--disabled")) { $(id).sortable("enable"); - $(id).find(".form-item--collapsable-stack").each(close); - $(id).find(".form-item--collapsable").addClass("form-item--collapsed"); + Object(__WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__["b" /* closeCollapsableStacks */])(id); } else { $(id).sortable("disable"); + Object(__WEBPACK_IMPORTED_MODULE_0__form_item_collapsable__["c" /* openCollapsableStacks */])(id); } $(id).toggleClass("sortable--disabled"); @@ -1416,5 +1011,232 @@ function updateSortable() { }); }); +/***/ }), +/* 14 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _FormItemCollapsable; }); +/* harmony export (immutable) */ __webpack_exports__["b"] = closeCollapsableStacks; +/* harmony export (immutable) */ __webpack_exports__["c"] = openCollapsableStacks; +/* harmony export (immutable) */ __webpack_exports__["d"] = resizeCollapsableStacks; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__select2__ = __webpack_require__(1); +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * FORM ITEM + */ + + + +// Component Global Variables +var newFormItemId = 1; + +var FormItemCollapsable = function () { + function FormItemCollapsable() { + _classCallCheck(this, FormItemCollapsable); + } + + _createClass(FormItemCollapsable, [{ + key: "isPresent", + value: function isPresent() { + if ($(".form--list").length > 0) { + return true; + } else { + return false; + } + } + }, { + key: "setEvents", + value: function setEvents() { + $(document).on("click", ".form--add-list-item", addNewItem); + $(document).on("click", ".form--delete-list-item", deleteItem); + $(document).on("click", ".form-item--collapse-btn", collapseToggle); + $(window).resize(resizeCollapsableStacks); + // Make sure all collapsable items are resized already at every page load + resizeCollapsableStacks(); + } + }]); + + return FormItemCollapsable; +}(); + +var _FormItemCollapsable = new FormItemCollapsable(); + +/** + * COMPONENT HELPER FUNCTIONS + */ + +/** + * Adds a new item. + * + * @param {event} event The event + */ +function addNewItem(event) { + // Stop default behaviour + event.preventDefault(); + // Get the child to clone + var id = $(this).data("id"); + var $list = $("#form--list-" + id); + var url = $(this).data("url"); + var data = $list.sortable("serialize"); + var params = $(this).data("params"); + if (params) { + data = data.concat("&" + params); + } + + $.post(url, data, function (data) { + // Get repaeter code from Rails + // Due to the Rails way of creating nested forms it's necessary to + // create the nested item inside a different new form, then get just + // the code contained between the two SPLIT comments + var parts = data.split(""); + var newItem = parts[1]; + setupAndAppend(newItem, $list); + }); +} + +/** + * Setup and append new item + * + * @param {string} newItem The new item + * @param {object} $list The list + */ +function setupAndAppend(newItem, $list) { + // Append the item + $list.prepend(newItem); + var collapsable = $list.find(".form-item--collapsable").get(0); + + // Update select input for Select2 plugin + Object(__WEBPACK_IMPORTED_MODULE_0__select2__["b" /* setupSelect2 */])($list.find("select")); + + // Setup TinyMCE for the newly created item + var textarea_editor_id = $list.find("textarea").last("textarea").attr("id"); + tinyMCE.EditorManager.execCommand("mceAddEditor", true, textarea_editor_id); + + // Prepare collapsable animation + collapsable.style.maxHeight = "0px"; + + // Prepare collapsable stack animation + openCollapsableStacks(collapsable); + + // Refresh Sortable to update the added item with Sortable features + $list.sortable("refresh"); + + // Run animation 500ms after previous style declaration (see above) otherwise animation doesn't get triggered + setTimeout(function () { + collapsable.style.maxHeight = collapsable.scrollHeight + "px"; + }, 50); +} + +/** + * Close collapsable stacks + * + * It closes all collapsable stacks inside the collapsable item passed as argument + * + * @param {object, string} target The target + */ +function closeCollapsableStacks(obj) { + $(obj).addClass("form-item--collapsed").find(".form-item--collapsable-stack").each(function () { + this.style.maxHeight = "0px"; + }); +} + +/** + * Open collapsable stacks + * + * It opens all collapsable stacks inside the collapsable item passed as argument + * + * @param {object, string} target The target + */ +function openCollapsableStacks(obj) { + // Don't execute this if sortable is enabled + if ($(obj).closest(".sortable").hasClass("sortable--enabled")) { + return; + } + $(obj).removeClass("form-item--collapsed").find(".form-item--collapsable-stack").each(function () { + this.style.maxHeight = this.scrollHeight + "px"; + }); +} + +/** + * Toggle a collapsable item + * + * Basically it opens it or closes it based on its state + * + * @param {event} event The event + */ +function collapseToggle(event) { + // Stop default behaviour + event.preventDefault(); + + var $collapsable = $(this).closest(".form-item--collapsable"); + + if ($collapsable.hasClass("form-item--collapsed")) { + openCollapsableStacks($collapsable); + } else { + closeCollapsableStacks($collapsable); + } +} + +/** + * Delete collapsable item and hide it + * + * @param {event} event The event + */ +function deleteItem(event) { + // Stop default behaviour + event.preventDefault(); + var record_id = $(this).data("id"); + var targetId = "#form--list-item-" + record_id; + var target = $(targetId).get(0); + // As max-height isn't set you need to set it manually before changing it, + // otherwise the animation doesn't get triggered + target.style.maxHeight = target.scrollHeight + "px"; + // Change max-height after 50ms to trigger css animation + setTimeout(function () { + target.style.maxHeight = "0px"; + }, 50); + + $.ajax({ + url: $(this).attr("href"), + data: { id: record_id, target_id: targetId, isAjax: true }, + method: "DELETE" + }).done(function (data) { + // Make sure the animation completes before removing the item (it should last 600ms + 50ms) + setTimeout(function () { + $(data.target_id).remove(); + }, 700); + }); + // TODO add a fallback if request fails +} + +/** + */ + +/** + * Resize all collapsable item + * + * If a target is passed as a argument the function will resize only that target and its children. + * + * @param {object, string} target The target + */ +function resizeCollapsableStacks(target) { + target = _.isUndefined(target) ? $(".form-item--collapsable-stack") : target; + $(target).each(function () { + // If the collapsable item is closed don't go any further + if ($(this).height() === 0 || $(this).closest(".form-item--collapsable").hasClass("form-item--collapsed")) { + this.style.maxHeight = "0px"; + } else { + // otherwise update the max-height which is needed for the CSS transition + // NOTE you need to remove the max-height (inside 'style' attribute) to get the real height + this.style.height = "auto"; + this.style.maxHeight = this.scrollHeight + "px"; + } + }); +} + /***/ }) /******/ ]); \ No newline at end of file diff --git a/app/assets/javascripts/binda/index.js b/app/assets/javascripts/binda/index.js index 37c54025..98d27535 100644 --- a/app/assets/javascripts/binda/index.js +++ b/app/assets/javascripts/binda/index.js @@ -2,11 +2,9 @@ /// INDEX OF BINDA'S SCRIPTS ///- - - - - - - - - - - - - - - - - - - - -import { _FormItem } from "./components/form_item"; -import { _FormItemRepeater } from "./components/form_item_repeater"; +import { _FormItemCollapsable } from "./components/form_item_collapsable"; import { _FormItemImage } from "./components/form_item_image"; import { _FieldSettingChoices } from "./components/field_setting_choices"; -import { _FormItemEditor } from "./components/form_item_editor"; import { _FileUpload } from "./components/fileupload"; import { _Shader } from "./components/login-shader"; import { _LoginForm } from "./components/login_form"; @@ -17,28 +15,22 @@ import setupSelect2 from "./components/select2"; import setupRadioToggle from "./components/radio-toggle"; $(document).ready(function() { - if (_FormItem.isSet()) { - _FormItem.setEvents(); + if (_FormItemCollapsable.isPresent()) { + _FormItemCollapsable.setEvents(); } - if (_FormItemRepeater.isSet()) { - _FormItemRepeater.setEvents(); - } - if (_FormItemImage.isSet()) { + if (_FormItemImage.isPresent()) { _FormItemImage.setEvents(); } - if (_FieldSettingChoices.isSet()) { + if (_FieldSettingChoices.isPresent()) { _FieldSettingChoices.setEvents(); } - if (_FormItemEditor.isSet()) { - _FormItemEditor.setEvents(); - } - if (_FileUpload.isSet()) { + if (_FileUpload.isPresent()) { _FileUpload.setEvents(); } - if (_LoginForm.isSet()) { + if (_LoginForm.isPresent()) { _LoginForm.init(); } - if (_Shader.isSet()) { + if (_Shader.isPresent()) { _Shader.setup(); _Shader.start(); } @@ -51,7 +43,7 @@ $(document).ready(function() { // handle event window.addEventListener("optimizedResize", function() { - if (_Shader.isSet()) { + if (_Shader.isPresent()) { _Shader.resize(); } }); diff --git a/app/assets/stylesheets/binda/components/form_item.scss b/app/assets/stylesheets/binda/components/form_item.scss index 9611e116..b1d9ff45 100644 --- a/app/assets/stylesheets/binda/components/form_item.scss +++ b/app/assets/stylesheets/binda/components/form_item.scss @@ -14,9 +14,11 @@ // float: left; // width: 50%; - // NEW SOLUTION (not very nice but plays nicely with max-height=0 and overflow-y=hidden) + // NEW SOLUTION (not very nice but plays nicely with max-height=0 and overflow-y=hidden) display: inline-block; - width: calc(50% - 3px); // 5px are for the weird issue caused by non-blank space around the inline-block elements + width: calc( + 50% - 3px + ); // 5px are for the weird issue caused by non-blank space around the inline-block elements .form-group { border-top: none !important; @@ -92,24 +94,24 @@ } .form-item--collapsable { - transition: box-shadow 0.3s ease, transform 0.3s ease, max-height 0.6s ease-out !important; + transition: box-shadow 0.3s ease, transform 0.3s ease, + max-height 0.6s ease-out !important; +} + +.form-item--collapsable-stack { + transition: box-shadow 0.3s ease, transform 0.3s ease, + max-height 0.6s ease-out !important; } .font-item--collapsed .form-item--collapsable-stack { margin-bottom: 8px; } -.form-item--add-new, -.form-item--repeater-section--add-new { +.form--add-list-item { float: right; margin-right: 8px; } -.form-item--repeater-title { - display: block; - margin-bottom: 20px; -} - .form-item--header { padding: 4px; background-color: $color-gray-lightest !important; @@ -121,16 +123,14 @@ } .form-item--collapse-btn, -.form-item--delete-repeater-item, -.form-item--delete-item, +.form--delete-list-item, .form-item--edit-item { float: right; margin-top: 8px; margin-right: 8px; } -a.form-item--delete-item, -a.form-item--delete-repeater-item { +a.form--delete-list-item { cursor: pointer; transition: color 0.2s ease; color: $color-danger; diff --git a/app/controllers/binda/boards_controller.rb b/app/controllers/binda/boards_controller.rb index 1baf0c52..c073f662 100644 --- a/app/controllers/binda/boards_controller.rb +++ b/app/controllers/binda/boards_controller.rb @@ -29,16 +29,25 @@ def destroy def new_repeater @repeater_setting = FieldSetting.find( params[:repeater_setting_id] ) - position = @instance.repeaters.find_all{|r| r.field_setting_id = @repeater_setting.id }.length + 1 - @repeater = @instance.repeaters.create( field_setting: @repeater_setting, position: position ) + @repeater = @instance.repeaters.create( field_setting: @repeater_setting ) + # Put new repeater to first position, then store all the other ones + if params[:repeater].nil? + repeaters = [ + @repeater.id.to_s, + *@instance.repeaters.select{|r| r.field_setting_id=@repeater_setting.id }.map(&:id) + ] + else + repeaters = [ + @repeater.id.to_s, + *params[:repeater]] + end + sort_repeaters_by(repeaters) render 'binda/fieldable/_form_item_new_repeater', layout: false end def sort_repeaters - params[:repeater].each_with_index do |id, i| - Repeater.find( id ).update({ position: i + 1 }) - end - head :ok + sort_repeaters_by(params[:repeater]) + render json: { id: "##{params[:id]}" }, status: 200 end def dashboard @@ -80,5 +89,14 @@ def board_params { structure_attributes: [ :id ] }, *fieldable_params ) end + + # Sort repeaters following the order with which are listed in the array provided as a argument. + # + # @param repeaters [Array] the list of ids of the repeaters + def sort_repeaters_by(repeaters) + repeaters.each_with_index do |id, i| + Repeater.find( id ).update!({ position: i }) + end + end end end diff --git a/app/controllers/binda/components_controller.rb b/app/controllers/binda/components_controller.rb index 93545614..22360b1a 100644 --- a/app/controllers/binda/components_controller.rb +++ b/app/controllers/binda/components_controller.rb @@ -80,7 +80,7 @@ def sort_repeaters def sort params[:component].each_with_index do |id, i| - Component.find( id ).update_column('position', i + 1) # use update_column to skip callbacks (which leads to huge useless memory consumption) + Component.find( id ).update_column('position', i) # use update_column to skip callbacks (which leads to huge useless memory consumption) end render json: { id: "##{params[:id]}" }, status: 200 end @@ -125,7 +125,7 @@ def component_params # @param repeaters [Array] the list of ids of the repeaters def sort_repeaters_by(repeaters) repeaters.each_with_index do |id, i| - Repeater.find( id ).update!({ position: i + 1 }) + Repeater.find( id ).update!({ position: i }) end end end diff --git a/app/controllers/binda/field_groups_controller.rb b/app/controllers/binda/field_groups_controller.rb index 1c4a0bbf..6e7e4e1e 100644 --- a/app/controllers/binda/field_groups_controller.rb +++ b/app/controllers/binda/field_groups_controller.rb @@ -3,7 +3,7 @@ module Binda class FieldGroupsController < ApplicationController before_action :set_structure - before_action :set_field_group, only: [:show, :edit, :update, :destroy, :new_field_setting] + before_action :set_field_group, only: [:show, :edit, :update, :destroy, :add_field_setting] def index redirect_to structure_field_group_path( @structure.slug ) @@ -53,19 +53,19 @@ def destroy def sort params[:field_group].each_with_index do |id, i| - FieldGroup.find( id ).update_column('position', i + 1) # use update_column to skip callbacks (which leads to huge useless memory consumption) + FieldGroup.find( id ).update_column('position', i ) # use update_column to skip callbacks (which leads to huge useless memory consumption) end render json: { id: "##{params[:id]}" }, status: 200 end def sort_field_settings params["form-item"].each_with_index do |id, i| - FieldSetting.find( id ).update_column('position', i + 1) # use update_column to skip callbacks (which leads to huge useless memory consumption) + FieldSetting.find( id ).update_column('position', i ) # use update_column to skip callbacks (which leads to huge useless memory consumption) end render json: { id: "##{params[:id]}" }, status: 200 end - def new_field_setting + def add_field_setting # We set some default values in order to be able to save the field setting # (if field setting isn't save it makes impossible to sort the order) @field_setting = FieldSetting.new( @@ -153,7 +153,7 @@ def update_field_setting_choices field_setting_params # @param field_settings [Array] the list of ids of the field settings def sort_field_setting_by(field_settings) field_settings.each_with_index do |id, i| - FieldSetting.find( id ).update!({ position: i + 1 }) + FieldSetting.find( id ).update!({ position: i }) end end end diff --git a/app/helpers/binda/field_groups_helper.rb b/app/helpers/binda/field_groups_helper.rb index 4ae08a31..5e44b672 100644 --- a/app/helpers/binda/field_groups_helper.rb +++ b/app/helpers/binda/field_groups_helper.rb @@ -1,12 +1,13 @@ module Binda module FieldGroupsHelper - + # Returns the right path for "new" or "edit" action def get_form_field_group_url return structure_field_groups_path if action_name == 'new' return structure_field_group_path if action_name == 'edit' end + # Get all components related to a "relation" field setting def get_relationable_components(field_setting) if @instance.class.to_s == 'Binda::Component' Binda::Component.where(structure_id: Binda::Structure.where(id: field_setting.accepted_structure_ids)).where.not(id: @instance.id) @@ -15,6 +16,7 @@ def get_relationable_components(field_setting) end end + # Get all boards related to a "relation" field setting def get_relationable_boards(field_setting) if @instance.class.to_s == 'Binda::Component' Binda::Board.where(structure_id: Binda::Structure.where(id: field_setting.accepted_structure_ids)) @@ -23,6 +25,7 @@ def get_relationable_boards(field_setting) end end + # Retrieve the number of records present in the database for the current structure def get_entries_number instance_type = @structure.instance_type if ['board', 'component'].include? instance_type diff --git a/app/views/binda/field_groups/_form_item.html.erb b/app/views/binda/field_groups/_form_item.html.erb index 8a49bf81..b5607bac 100644 --- a/app/views/binda/field_groups/_form_item.html.erb +++ b/app/views/binda/field_groups/_form_item.html.erb @@ -1,3 +1,5 @@ -<%# DON'T USE ff.input :position otherwise it will override the order set by Binda via ajax when adding a new item %> -<%= render 'binda/field_groups/form_item/form_item_header', ff: ff %> -<%= render 'binda/field_groups/form_item/form_item_editor', f: f, ff: ff, isNew: isNew %> \ No newline at end of file +
  • <%= "form-item--new" if is_new %> <%= "form-item--collapsed" unless is_new %>"> + <%= render 'binda/field_groups/form_item/form_item_header', ff: ff %> + <%= render 'binda/field_groups/form_item/form_item_editor', f: f, ff: ff, is_new: is_new %> +
  • \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_new_item.html.erb b/app/views/binda/field_groups/_form_new_item.html.erb index 07b6876a..3f070396 100644 --- a/app/views/binda/field_groups/_form_new_item.html.erb +++ b/app/views/binda/field_groups/_form_new_item.html.erb @@ -1,10 +1,8 @@ <%= simple_form_for [@field_group.structure, @field_group], defaults: { label: false }, html: { multipart: true } do |f| %> - <%= f.simple_fields_for "field_settings_attributes[]", @field_setting do |ff| %> -
  • - <%= render 'form_item', f: f, ff: ff, isNew: true %> -
  • + <%# see https://stackoverflow.com/questions/32984498/rails-4-adding-child-index-to-dynamically-added-nested-form-fields-with-cocoo %> + <%= f.simple_fields_for :field_settings, @field_setting, child_index: (Time.now + rand(10000)).to_i do |ff| %> + <%= render 'binda/field_groups/form_item', f: f, ff: ff, is_new: true %> <% end %> <% end %> \ No newline at end of file diff --git a/app/views/binda/field_groups/_form_section_repeater.html.erb b/app/views/binda/field_groups/_form_section_repeater.html.erb index 1f43368a..aca77223 100644 --- a/app/views/binda/field_groups/_form_section_repeater.html.erb +++ b/app/views/binda/field_groups/_form_section_repeater.html.erb @@ -1,5 +1,5 @@
    -
    diff --git a/app/views/binda/field_groups/form_item/_form_item_editor.html.erb b/app/views/binda/field_groups/form_item/_form_item_editor.html.erb index 512b27d2..38ab6491 100644 --- a/app/views/binda/field_groups/form_item/_form_item_editor.html.erb +++ b/app/views/binda/field_groups/form_item/_form_item_editor.html.erb @@ -1,8 +1,8 @@
    - <%= ff.input :name, as: :string, input_html: { class: "form-item--input" } %> + <%= ff.input :name, as: :string, input_html: { class: "form-item--default-input", "test-hook": "field_group_field_settings_name" } %>
    - <% if isNew %> + <% if is_new %> <%= render 'binda/field_groups/form_item/form_item_editor_new_item', ff: ff %> <% else %> <%= render 'binda/field_groups/form_item/form_item_editor_existing_item', ff: ff %> @@ -13,5 +13,6 @@ <%= ff.input :field_group_id, as: :hidden %> <%= ff.input :id, as: :hidden %> <%= ff.input :ancestry, as: :hidden unless ff.object.is_root? %> + <%# DON'T USE ff.input :position otherwise it will override the order set by Binda via ajax when adding a new item %>
    \ No newline at end of file diff --git a/app/views/binda/field_groups/form_item/_form_item_header.html.erb b/app/views/binda/field_groups/form_item/_form_item_header.html.erb index 008146b0..5e1800b0 100644 --- a/app/views/binda/field_groups/form_item/_form_item_header.html.erb +++ b/app/views/binda/field_groups/form_item/_form_item_header.html.erb @@ -1,10 +1,18 @@
    - - <%= t('binda.collapse') %> - <%= t('binda.expand') %> + + + + <%= t('binda.collapse') %> + + + + <%= t('binda.expand') %> + diff --git a/app/views/binda/field_groups/form_section/_form_section_header.html.erb b/app/views/binda/field_groups/form_section/_form_section_header.html.erb index b9dedd29..18b34d53 100644 --- a/app/views/binda/field_groups/form_section/_form_section_header.html.erb +++ b/app/views/binda/field_groups/form_section/_form_section_header.html.erb @@ -1,7 +1,7 @@ -
    +
    + data-sortable-target-id="form--list-<%= defined?(repeater) ? repeater.id : f.object.id %>"> <%= t'binda.done' %> @@ -12,10 +12,10 @@ <% if defined? repeater %> - @@ -23,10 +23,10 @@ <%= t('binda.field_setting.singular') %> <% else %> - diff --git a/app/views/binda/field_groups/form_section/_form_section_list.html.erb b/app/views/binda/field_groups/form_section/_form_section_list.html.erb index 4752a3c9..3cd89f1c 100644 --- a/app/views/binda/field_groups/form_section/_form_section_list.html.erb +++ b/app/views/binda/field_groups/form_section/_form_section_list.html.erb @@ -1,12 +1,9 @@ -
      <% ancestry = defined?(repeater) ? repeater.id.to_s : nil %> <%= f.simple_fields_for :field_settings, f.object.field_settings.where( ancestry: ancestry ).order( :position, :name ) do |ff| %> -
    • form-item--collapsable form-item--collapsed"> - <%= render 'form_item', f: f, ff: ff, isNew: false %> -
    • + <%= render 'binda/field_groups/form_item', f: f, ff: ff, is_new: false %> <% end %>
    \ No newline at end of file diff --git a/app/views/binda/fieldable/_form_item_new_repeater.html.erb b/app/views/binda/fieldable/_form_item_new_repeater.html.erb index 5a1c4c94..53c145da 100644 --- a/app/views/binda/fieldable/_form_item_new_repeater.html.erb +++ b/app/views/binda/fieldable/_form_item_new_repeater.html.erb @@ -1,8 +1,8 @@ <%= simple_form_for [@instance.structure, @instance], defaults: { label: false }, html: { multipart: true } do |f| %> - <%= render 'binda/fieldable/form_item_repeater/form_item_repeater_list_item', - repeater_setting: @repeater.field_setting, - repeater: @repeater, - f: f %> + <%# see https://stackoverflow.com/questions/32984498/rails-4-adding-child-index-to-dynamically-added-nested-form-fields-with-cocoo %> + <%= f.simple_fields_for "relations_attributes[]", @repeater, child_index: (Time.now * rand(10000)).to_i do |ff| %> + <%= render 'binda/fieldable/form_item_relation', ff: ff, field_setting: @repeater.field_setting %> + <% end %> <% end %> \ No newline at end of file diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb index 5e84eca6..95bd4bb9 100644 --- a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_header.html.erb @@ -1,7 +1,5 @@ -
    - +
    + <%= t'binda.done' %> @@ -12,10 +10,11 @@ + data-id="<%= repeater_setting.id %>" + data-params="repeater_setting_id=<%= repeater_setting.id %>"> <%= t('binda.new_item_in_repeater', arg1: repeater_setting.name ) %> diff --git a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb index 604b7c06..ae60e87f 100644 --- a/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb +++ b/app/views/binda/fieldable/form_item_repeater/_form_item_repeater_list.html.erb @@ -1,7 +1,8 @@ -