From 78b54a10021c97ca86e6ba6ec211f707135c8b2e Mon Sep 17 00:00:00 2001 From: John Bintz Date: Wed, 17 Oct 2012 10:09:53 -0400 Subject: [PATCH 01/13] hack in formtastic input --- Gemfile | 2 +- Rakefile | 2 +- lib/cocoon.rb | 6 +++++- lib/cocoon/formtastic/cocoon_input.rb | 24 ++++++++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 lib/cocoon/formtastic/cocoon_input.rb diff --git a/Gemfile b/Gemfile index e857791..fe80d9b 100644 --- a/Gemfile +++ b/Gemfile @@ -16,4 +16,4 @@ end # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+) # gem 'ruby-debug' -# gem 'ruby-debug19' \ No newline at end of file +# gem 'ruby-debug19' diff --git a/Rakefile b/Rakefile index 8b35f85..a09d6cb 100644 --- a/Rakefile +++ b/Rakefile @@ -39,4 +39,4 @@ begin Jeweler::GemcutterTasks.new rescue LoadError puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" -end \ No newline at end of file +end diff --git a/lib/cocoon.rb b/lib/cocoon.rb index ec9039c..3570373 100644 --- a/lib/cocoon.rb +++ b/lib/cocoon.rb @@ -10,7 +10,11 @@ module Cocoon # configure our plugin on boot initializer "cocoon.initialize" do |app| ActionView::Base.send :include, Cocoon::ViewHelpers + + if Object.const_defined?("Formtastic") and Formtastic.const_defined?("Inputs") + require 'cocoon/formtastic/cocoon_input' + end end end -end \ No newline at end of file +end diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb new file mode 100644 index 0000000..209f157 --- /dev/null +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -0,0 +1,24 @@ +require 'formtastic' + +class CocoonInput + include ::Formtastic::Inputs::Base + + def to_html + output = [] + + output << label_html + + output << builder.semantic_fields_for(method) do |fields| + if fields.object + template.render :partial => "#{method.to_s.singularize}_fields", :locals => { :f => fields } + end + end + + output << template.content_tag(:div, :class => 'links') do + template.link_to_add_association template.t('.add'), builder, method + end + + template.content_tag(:li, output.join('').html_safe, :class => 'input cocoon') + end +end + -- 2.40.1 From 3f89fff3644f97be537e3d33b36e43408ba328b2 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Wed, 17 Oct 2012 11:21:19 -0400 Subject: [PATCH 02/13] add stylesheet for active admin integration --- .gitignore | 1 + .../stylesheets/cocoon/active_admin.css.scss | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 app/assets/stylesheets/cocoon/active_admin.css.scss diff --git a/.gitignore b/.gitignore index fa4ca05..25d51bd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ spec/dummy/tmp/ .idea coverage/ Gemfile.lock +.sass-cache/ diff --git a/app/assets/stylesheets/cocoon/active_admin.css.scss b/app/assets/stylesheets/cocoon/active_admin.css.scss new file mode 100644 index 0000000..7fb1e61 --- /dev/null +++ b/app/assets/stylesheets/cocoon/active_admin.css.scss @@ -0,0 +1,22 @@ +li.cocoon { + & > label { + display: block; + width: 100%; + float: none; + } + + ol { + padding: 0; + width: auto; + float: none; + } + + fieldset { + padding: 1em; + } + + .links a { + @extend .button; + } +} + -- 2.40.1 From f584e181d26beb54af896e95847d0605e339cf0f Mon Sep 17 00:00:00 2001 From: John Bintz Date: Thu, 18 Oct 2012 14:02:10 -0400 Subject: [PATCH 03/13] ensure important --- .../stylesheets/cocoon/active_admin.css.scss | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/cocoon/active_admin.css.scss b/app/assets/stylesheets/cocoon/active_admin.css.scss index 7fb1e61..1399ad1 100644 --- a/app/assets/stylesheets/cocoon/active_admin.css.scss +++ b/app/assets/stylesheets/cocoon/active_admin.css.scss @@ -1,18 +1,18 @@ li.cocoon { & > label { - display: block; - width: 100%; - float: none; + display: block !important; + width: 100% !important; + float: none !important; } ol { - padding: 0; - width: auto; - float: none; + padding: 0 !important; + width: auto !important; + float: none !important; } fieldset { - padding: 1em; + padding: 1em !important; } .links a { -- 2.40.1 From 663a9f344c6c066c5e0df3ae74a0cf14ced8af5e Mon Sep 17 00:00:00 2001 From: John Bintz Date: Sun, 21 Oct 2012 13:12:29 -0400 Subject: [PATCH 04/13] some fixes --- lib/cocoon/formtastic/cocoon_input.rb | 4 ++-- lib/cocoon/view_helpers.rb | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb index 209f157..ab28893 100644 --- a/lib/cocoon/formtastic/cocoon_input.rb +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -15,10 +15,10 @@ class CocoonInput end output << template.content_tag(:div, :class => 'links') do - template.link_to_add_association template.t('.add'), builder, method + template.link_to_add_association template.t('.add'), builder, method, input_html_options end - template.content_tag(:li, output.join('').html_safe, :class => 'input cocoon') + template.content_tag(:li, output.join('').html_safe, wrapper_html_options) end end diff --git a/lib/cocoon/view_helpers.rb b/lib/cocoon/view_helpers.rb index f83a207..c059263 100644 --- a/lib/cocoon/view_helpers.rb +++ b/lib/cocoon/view_helpers.rb @@ -35,6 +35,7 @@ module Cocoon partial = get_partial_path(custom_partial, association) locals = render_options.delete(:locals) || {} method_name = f.respond_to?(:semantic_fields_for) ? :semantic_fields_for : (f.respond_to?(:simple_fields_for) ? :simple_fields_for : :fields_for) + f.send(method_name, association, new_object, {:child_index => "new_#{association}"}.merge(render_options)) do |builder| partial_options = {:f => builder, :dynamic => true}.merge(locals) render(partial, partial_options) -- 2.40.1 From bfe72bd29f1b461c737cd71ece2cf60ce42bea35 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Thu, 25 Oct 2012 14:49:55 -0400 Subject: [PATCH 05/13] add support for sorting nested forms --- README.markdown | 56 +++++++++++++++++++ app/assets/javascripts/cocoon.js | 4 +- app/assets/javascripts/cocoon/ordered.js | 29 ++++++++++ .../stylesheets/cocoon/active_admin.css.scss | 7 +++ lib/cocoon/formtastic/cocoon_input.rb | 7 ++- 5 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/cocoon/ordered.js diff --git a/README.markdown b/README.markdown index d6c93f8..6d1bff1 100644 --- a/README.markdown +++ b/README.markdown @@ -16,6 +16,8 @@ This project is not related to [Apache Cocoon](http://cocoon.apache.org/) This gem uses jQuery, it is most useful to use this gem in a rails3 project where you are already using jQuery. +Sortable form support requires jQuery UI. + Furthermore i would advice you to use either formtastic or simple_form. I have a sample project where I demonstrate the use of cocoon with formtastic. @@ -37,6 +39,13 @@ asset_pipeline //= require cocoon ```` +If you also want to be able to sort nested forms, ordering them on a particular field, add `cocoon/ordered`: + +``` ruby +//= require cocoon +//= require cocoon/ordered +``` + ### Rails 3.0.x If you are using Rails 3.0.x, you need to run the installation task (since rails 3.1 this is no longer needed): @@ -125,6 +134,53 @@ That is all there is to it! There is an example project on github implementing it called [cocoon_formtastic_demo](https://github.com/nathanvda/cocoon_formtastic_demo). +Or, you can use the Formtastic `cocoon` field type to wrap up much of the boilerplate of the wrapper and +add association button: + +``` haml += f.inputs do + = f.input :name + = f.input :description + %h3 Tasks + #tasks + = f.input :tasks, :as => :cocoon + = f.actions do + = f.action :submit +``` + +#### Sortable forms + +Say you have a set of nested models that are ordered arbitrarily: + +``` ruby +class Task < ActiveRecord::Base + belongs_to :project + + default_scope :order => 'order ASC' +end +``` + +You want users to be able to sort those +models via the UI. You can do this by including `cocoon/ordered` and specifying the sort field in the Formtastic +input call: + +``` haml += f.input :tasks, :as => :cocoon, :ordered_by => :order +``` + +Add the order field as a hidden field in the nested form: + +``` haml +.nested-fields + = f.inputs do + = f.input :description + = f.input :done, :as => :boolean + = f.input :order, :as => :hidden + = link_to_remove_association "remove task", f +``` + +The order field will now be filled in correctly when new models are added and when the models are sorted. + ### Using simple_form Inside our `projects/_form` partial we then write: diff --git a/app/assets/javascripts/cocoon.js b/app/assets/javascripts/cocoon.js index 2418e6f..e41d296 100644 --- a/app/assets/javascripts/cocoon.js +++ b/app/assets/javascripts/cocoon.js @@ -51,14 +51,14 @@ var contentNode = $(new_content); - insertionNode.trigger('cocoon:before-insert'); + insertionNode.trigger('cocoon:before-insert', contentNode); // allow any of the jquery dom manipulation methods (after, before, append, prepend, etc) // to be called on the node. allows the insertion node to be the parent of the inserted // code and doesn't force it to be a sibling like after/before does. default: 'before' insertionNode[insertionMethod](contentNode); - insertionNode.trigger('cocoon:after-insert'); + insertionNode.trigger('cocoon:after-insert', contentNode); }); $('.remove_fields.dynamic').live('click', function(e) { diff --git a/app/assets/javascripts/cocoon/ordered.js b/app/assets/javascripts/cocoon/ordered.js new file mode 100644 index 0000000..37ed84a --- /dev/null +++ b/app/assets/javascripts/cocoon/ordered.js @@ -0,0 +1,29 @@ +//= require jquery-ui +// +(function($) { + $(function() { + $('li[data-ordered_by]').each(function(index, element) { + var field = $(element).data('ordered_by'); + var fieldSearch = "[name*='[" + field + "]']" + + $(element).sortable({ + items: '.nested-fields', + stop: function(e, ui) { + $(element).find(fieldSearch).each(function(index, element) { + $(element).val(index); + }); + } + }); + + $(element).bind('cocoon:after-insert', function(e, node) { + var nextOrder = 0; + + $(element).find(fieldSearch).each(function() { + nextOrder = Math.max(nextOrder, Number($(this).val()) + 1); + }); + + $(node).find(fieldSearch).val(nextOrder) + }); + }); + }); +})(jQuery); diff --git a/app/assets/stylesheets/cocoon/active_admin.css.scss b/app/assets/stylesheets/cocoon/active_admin.css.scss index 1399ad1..3678246 100644 --- a/app/assets/stylesheets/cocoon/active_admin.css.scss +++ b/app/assets/stylesheets/cocoon/active_admin.css.scss @@ -18,5 +18,12 @@ li.cocoon { .links a { @extend .button; } + + &.ui-sortable { + fieldset { + border-top: solid #777 12px; + cursor: move; + } + } } diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb index 209f157..649782e 100644 --- a/lib/cocoon/formtastic/cocoon_input.rb +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -18,7 +18,12 @@ class CocoonInput template.link_to_add_association template.t('.add'), builder, method end - template.content_tag(:li, output.join('').html_safe, :class => 'input cocoon') + data = { :class => 'input cocoon' } + if options[:ordered_by] + data['data-ordered_by'] = options[:ordered_by] + end + + template.content_tag(:li, output.join('').html_safe, data) end end -- 2.40.1 From 042fcc997699f192a217d6dd12644f387da9ed3c Mon Sep 17 00:00:00 2001 From: John Bintz Date: Thu, 25 Oct 2012 15:02:00 -0400 Subject: [PATCH 06/13] fix dumb bug --- lib/cocoon/formtastic/cocoon_input.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb index f07dd1d..9198f94 100644 --- a/lib/cocoon/formtastic/cocoon_input.rb +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -18,7 +18,7 @@ class CocoonInput template.link_to_add_association template.t('.add'), builder, method, input_html_options end - data = wraper_html_options.merge(:class => 'input cocoon') + data = wrapper_html_options.merge(:class => 'input cocoon') if options[:ordered_by] data['data-ordered_by'] = options[:ordered_by] end -- 2.40.1 From b8f373fd5e1a3a7ce99c8918541b71360051b1c3 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Thu, 25 Oct 2012 15:17:47 -0400 Subject: [PATCH 07/13] add the most basic sanity tests for formtastic input --- Gemfile | 1 + lib/cocoon/formtastic/cocoon_input.rb | 34 ++++++++----- spec/formtastic/cocoon_input_spec.rb | 73 +++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 spec/formtastic/cocoon_input_spec.rb diff --git a/Gemfile b/Gemfile index fe80d9b..bfbfb17 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ group :development, :test do gem "simplecov", :require => false gem "generator_spec" + gem "formtastic" end # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+) diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb index 9198f94..8e2cbdb 100644 --- a/lib/cocoon/formtastic/cocoon_input.rb +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -4,26 +4,32 @@ class CocoonInput include ::Formtastic::Inputs::Base def to_html - output = [] + output = label_html << semantic_fields_for << links - output << label_html + template.content_tag(:li, output.html_safe, wrapper_html_options) + end - output << builder.semantic_fields_for(method) do |fields| - if fields.object - template.render :partial => "#{method.to_s.singularize}_fields", :locals => { :f => fields } - end - end - - output << template.content_tag(:div, :class => 'links') do - template.link_to_add_association template.t('.add'), builder, method, input_html_options - end - - data = wrapper_html_options.merge(:class => 'input cocoon') + def wrapper_html_options + data = super.merge(:class => 'input cocoon') if options[:ordered_by] data['data-ordered_by'] = options[:ordered_by] end - template.content_tag(:li, output.join('').html_safe, data) + data + end + + def semantic_fields_for + builder.semantic_fields_for(method) do |fields| + if fields.object + template.render :partial => "#{method.to_s.singularize}_fields", :locals => { :f => fields } + end + end + end + + def links + template.content_tag(:div, :class => 'links') do + template.link_to_add_association template.t('.add'), builder, method, input_html_options + end end end diff --git a/spec/formtastic/cocoon_input_spec.rb b/spec/formtastic/cocoon_input_spec.rb new file mode 100644 index 0000000..67e34a0 --- /dev/null +++ b/spec/formtastic/cocoon_input_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' +require 'cocoon/formtastic/cocoon_input' + +describe CocoonInput do + let(:input) { CocoonInput.new(builder, template, object, object_name, method, options) } + let(:builder) { stub(:auto_index => false, :options => {}, :custom_namespace => nil, :all_fields_required_by_default => false) } + let(:template) { stub } + let(:object) { stub } + let(:object_name) { :object } + let(:method) { :nested } + let(:options) { {} } + + describe '#wrapper_html_options' do + subject { input.wrapper_html_options } + + context 'not ordered' do + it 'should not be ordered' do + subject.should_not have_key('data-ordered_by') + end + end + + context 'ordered' do + let(:field) { :field } + let(:options) { { :ordered_by => field } } + + it 'should be ordered' do + subject['data-ordered_by'].should == field + end + end + end + + describe '#links' do + subject { input.links } + + before do + template.stub(:content_tag).and_yield + template.stub(:link_to_add_association) + template.stub(:t) + end + + it 'should generate the links holder' do + subject + end + end + + describe '#semantic_fields_for' do + subject { input.semantic_fields_for } + + before do + builder.stub(:semantic_fields_for) + template.stub(:render) + end + + it 'should pass through to semantic_fields_for on the builder' do + subject + end + end + + describe '#to_html' do + subject { input.to_html } + + before do + input.stub(:label_html).and_return('label') + input.stub(:semantic_fields_for).and_return('fields') + input.stub(:links).and_return('links') + template.stub(:content_tag) + end + + it 'should concatenate the outputs and pass through to content_tag' do + subject + end + end +end -- 2.40.1 From 511184a26701103b3278c1e7ac31c89e13264a86 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Thu, 25 Oct 2012 15:27:25 -0400 Subject: [PATCH 08/13] fix merge problem --- app/assets/javascripts/cocoon.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/cocoon.js b/app/assets/javascripts/cocoon.js index 509a2ca..2cedf00 100644 --- a/app/assets/javascripts/cocoon.js +++ b/app/assets/javascripts/cocoon.js @@ -44,7 +44,7 @@ var contentNode = $(new_content); - insertionNode.trigger('cocoon:before-insert', contentNode); + insertionNode.trigger('cocoon:before-insert', [contentNode]); // allow any of the jquery dom manipulation methods (after, before, append, prepend, etc) // to be called on the node. allows the insertion node to be the parent of the inserted @@ -67,7 +67,8 @@ var timeout = trigger_node.data('remove-timeout') || 0; - setTimeout(function() { + setTimeout( + function() { if ($this.hasClass('dynamic')) { $this.closest(".nested-fields").remove(); } else { -- 2.40.1 From a1715f049ba6f710342b30927e591e10b1d9380b Mon Sep 17 00:00:00 2001 From: John Bintz Date: Fri, 26 Oct 2012 17:59:07 -0400 Subject: [PATCH 09/13] more work to make nested sortable forms work, can now sort forms within forms --- app/assets/javascripts/cocoon.js | 3 + app/assets/javascripts/cocoon/ordered.js | 97 +++++++++++++++---- .../stylesheets/cocoon/active_admin.css.scss | 2 +- lib/cocoon/formtastic/cocoon_input.rb | 14 ++- 4 files changed, 95 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/cocoon.js b/app/assets/javascripts/cocoon.js index 2cedf00..b45c389 100644 --- a/app/assets/javascripts/cocoon.js +++ b/app/assets/javascripts/cocoon.js @@ -7,6 +7,9 @@ content.replace(reg_exp, with_str); } + $.fn.parentSiblings = function(selector) { + return $(this).parent().siblings(selector); + } $('.add_fields').live('click', function(e) { e.preventDefault(); diff --git a/app/assets/javascripts/cocoon/ordered.js b/app/assets/javascripts/cocoon/ordered.js index 37ed84a..99a9ffc 100644 --- a/app/assets/javascripts/cocoon/ordered.js +++ b/app/assets/javascripts/cocoon/ordered.js @@ -1,29 +1,88 @@ //= require jquery-ui // (function($) { - $(function() { - $('li[data-ordered_by]').each(function(index, element) { - var field = $(element).data('ordered_by'); - var fieldSearch = "[name*='[" + field + "]']" - - $(element).sortable({ - items: '.nested-fields', + $.cocoon = { + ordered: { + options: { + items: '> .nested-fields', stop: function(e, ui) { - $(element).find(fieldSearch).each(function(index, element) { - $(element).val(index); - }); + if (window.CKEDITOR) { + var editors = $(ui.item).data('cocoon_ckeditors'); + var i, j; + + for (i = 0, j = editors.length; i < j; ++i) { + var id = editors[i]; + var editor = CKEDITOR.instances[id]; + + if (editor) { + editor.destroy(true); + CKEDITOR.remove(id); + } + + CKEDITOR.replace(id); + } + + $(ui.item).data('cocoon_ckeditors', []); + } + + $.cocoon.ordered._updateFields(this) + }, + start: function(e, ui) { + if (window.CKEDITOR) { + var editors = []; + + $(ui.item).find('textarea').each(function(index, element) { + var id = $(element).attr('id'); + var editor = CKEDITOR.instances[id]; + if (editor) { + editors.push(id); + + editor.destroy(); + CKEDITOR.remove(id); + } + }); + + $(ui.item).data('cocoon_ckeditors', editors); + } } - }); + }, + _updateFields: function(element) { + console.log(element) + console.log($(element).data('fieldSearch')) + console.log($(element).find($(element).data('fieldSearch'))) - $(element).bind('cocoon:after-insert', function(e, node) { - var nextOrder = 0; + $(element).find($(element).data('fieldSearch')).each(function(index, element) { + $(element).val(index); + }); + }, + setup: function() { + $('li[data-ordered_by]').each(function(index, element) { + var field = $(element).data('ordered_by'); + var fieldSelector = "[name*='[" + field + "]']" + var fieldGroupSelector = "> .forms > .nested-fields" + var orderFieldSelector = "> .nested-fields " + fieldSelector; + var fieldSearch = "> .forms " + orderFieldSelector; - $(element).find(fieldSearch).each(function() { - nextOrder = Math.max(nextOrder, Number($(this).val()) + 1); + $(element).find('.forms').data('fieldSearch', orderFieldSelector).sortable($.cocoon.ordered.options); + + $(element).unbind('cocoon:after-insert').bind('cocoon:after-insert', function(e, node) { + var nextOrder = 0; + + if ($(element).find(fieldGroupSelector).is(node)) { + $(element).find(fieldSearch).each(function() { + nextOrder = Math.max(nextOrder, Number($(this).val())); + }); + + $(node).find(fieldSelector).val(nextOrder + 1) + } + }); }); - $(node).find(fieldSearch).val(nextOrder) - }); - }); - }); + $(document).on('cocoon:after-insert', function() { $.cocoon.ordered.setup(); }); + } + }, + }; + + $(function() { $.cocoon.ordered.setup(); }); })(jQuery); + diff --git a/app/assets/stylesheets/cocoon/active_admin.css.scss b/app/assets/stylesheets/cocoon/active_admin.css.scss index 3678246..e685d2a 100644 --- a/app/assets/stylesheets/cocoon/active_admin.css.scss +++ b/app/assets/stylesheets/cocoon/active_admin.css.scss @@ -19,7 +19,7 @@ li.cocoon { @extend .button; } - &.ui-sortable { + .forms.ui-sortable { fieldset { border-top: solid #777 12px; cursor: move; diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb index 8e2cbdb..ae2a67a 100644 --- a/lib/cocoon/formtastic/cocoon_input.rb +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -4,7 +4,7 @@ class CocoonInput include ::Formtastic::Inputs::Base def to_html - output = label_html << semantic_fields_for << links + output = label_html << wrapped_semantic_fields << links template.content_tag(:li, output.html_safe, wrapper_html_options) end @@ -26,10 +26,22 @@ class CocoonInput end end + def wrapped_semantic_fields + template.content_tag(:div, semantic_fields_for, :class => 'forms') + end + def links template.content_tag(:div, :class => 'links') do template.link_to_add_association template.t('.add'), builder, method, input_html_options end end + + def input_html_options + super.merge( + 'data-association-insertion-node' => '.forms', + 'data-association-insertion-traversal' => 'parentSiblings', + 'data-association-insertion-method' => 'append' + ) + end end -- 2.40.1 From 09fe15c446aab1b61eb77e5053fc3e11d8fbf91d Mon Sep 17 00:00:00 2001 From: John Bintz Date: Fri, 26 Oct 2012 17:59:48 -0400 Subject: [PATCH 10/13] fix broken test --- spec/formtastic/cocoon_input_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/formtastic/cocoon_input_spec.rb b/spec/formtastic/cocoon_input_spec.rb index 67e34a0..e85a859 100644 --- a/spec/formtastic/cocoon_input_spec.rb +++ b/spec/formtastic/cocoon_input_spec.rb @@ -61,7 +61,7 @@ describe CocoonInput do before do input.stub(:label_html).and_return('label') - input.stub(:semantic_fields_for).and_return('fields') + input.stub(:wrapped_semantic_fields).and_return('fields') input.stub(:links).and_return('links') template.stub(:content_tag) end -- 2.40.1 From 83beb92d4a0899ac94f7528b55ee2816f990bc4e Mon Sep 17 00:00:00 2001 From: John Bintz Date: Thu, 1 Nov 2012 13:19:36 -0400 Subject: [PATCH 11/13] refactor and make i18n nicer --- lib/cocoon/formtastic/cocoon_input.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/cocoon/formtastic/cocoon_input.rb b/lib/cocoon/formtastic/cocoon_input.rb index ae2a67a..e9160d7 100644 --- a/lib/cocoon/formtastic/cocoon_input.rb +++ b/lib/cocoon/formtastic/cocoon_input.rb @@ -21,7 +21,7 @@ class CocoonInput def semantic_fields_for builder.semantic_fields_for(method) do |fields| if fields.object - template.render :partial => "#{method.to_s.singularize}_fields", :locals => { :f => fields } + template.render :partial => "#{singular_method}_fields", :locals => { :f => fields } end end end @@ -32,7 +32,7 @@ class CocoonInput def links template.content_tag(:div, :class => 'links') do - template.link_to_add_association template.t('.add'), builder, method, input_html_options + template.link_to_add_association template.t(".add_#{singular_method}"), builder, method, input_html_options end end @@ -43,5 +43,10 @@ class CocoonInput 'data-association-insertion-method' => 'append' ) end + + private + def singular_method + @singular_method ||= method.to_s.singularize + end end -- 2.40.1 From 6d85276a4e40dc5cf6c9da9ec18abc8fc74d19c2 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Tue, 27 Nov 2012 10:17:53 -0500 Subject: [PATCH 12/13] add cocoon wrapper --- lib/cocoon/view_helpers.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/cocoon/view_helpers.rb b/lib/cocoon/view_helpers.rb index c059263..3309aad 100644 --- a/lib/cocoon/view_helpers.rb +++ b/lib/cocoon/view_helpers.rb @@ -100,6 +100,14 @@ module Cocoon partial ? partial : association.to_s.singularize + "_fields" end + def cocoon_wrapper(form_builder, &block) + content_tag(:div, :class => 'nested-fields') do + content_tag(:fieldset) do + capture(&block) << link_to_remove_association(t('.remove'), form_builder) + end + end + end + private def create_object_on_non_association(f, association) -- 2.40.1 From 00643beceb3041add84b249d5051777dfc7931e9 Mon Sep 17 00:00:00 2001 From: Ari Epstein Date: Sat, 8 Dec 2012 18:17:26 -0500 Subject: [PATCH 13/13] on stop set first item position at 1, not 0 --- app/assets/javascripts/cocoon/ordered.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/cocoon/ordered.js b/app/assets/javascripts/cocoon/ordered.js index 99a9ffc..f3e1964 100644 --- a/app/assets/javascripts/cocoon/ordered.js +++ b/app/assets/javascripts/cocoon/ordered.js @@ -52,7 +52,7 @@ console.log($(element).find($(element).data('fieldSearch'))) $(element).find($(element).data('fieldSearch')).each(function(index, element) { - $(element).val(index); + $(element).val(index + 1); }); }, setup: function() { -- 2.40.1