cocooned 1.4.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1 @@
1
-
2
- .cocooned-visible-item {
3
- transition: opacity .45s ease-out;
4
- opacity: 1;
5
- }
6
-
7
- .cocooned-hidden-item {
8
- opacity: 0;
9
- }
1
+ /* TODO: Remove in 3.0 */
data/cocooned.gemspec CHANGED
@@ -11,32 +11,41 @@ Gem::Specification.new do |spec|
11
11
  spec.authors = ['Gaël-Ian Havard', 'Nathan Van der Auwera']
12
12
  spec.email = ['gael-ian@notus.sh', 'nathan@dixis.com']
13
13
 
14
- spec.summary = 'Unobtrusive Rails nested forms handling, with or without jQuery.'
15
- spec.description = 'Easier nested form. Supports standard Rails forms, Formtastic and SimpleForm.'
16
- spec.homepage = 'http://github.com/notus-sh/cocooned'
14
+ spec.summary = 'Form builder agnostic handling of Rails nested forms'
15
+ spec.description = <<-DESC.gsub(/\s+/, ' ')
16
+ Easier nested form in Rails with capabilities to add, remove, reorder or limit nested items.
17
+ Works with standard Rails form builder, Formtastic or SimpleForm, and with or without jQuery.
18
+ DESC
19
+ spec.homepage = 'https://github.com/notus-sh/cocooned'
17
20
 
18
21
  raise 'RubyGems 2.0 or newer is required.' unless spec.respond_to?(:metadata)
19
22
 
20
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
23
+ spec.metadata = {
24
+ 'allowed_push_host' => 'https://rubygems.org',
25
+ 'rubygems_mfa_required' => 'true',
26
+
27
+ 'bug_tracker_uri' => 'https://github.com/notus-sh/cocooned/issues',
28
+ 'changelog_uri' => 'https://github.com/notus-sh/cocooned/blob/main/CHANGELOG.md',
29
+ 'homepage_uri' => 'https://github.com/notus-sh/cocooned',
30
+ 'source_code_uri' => 'https://github.com/notus-sh/cocooned',
31
+ 'funding_uri' => 'https://opencollective.com/notus-sh'
32
+ }
21
33
 
22
34
  spec.require_paths = ['lib']
35
+
36
+ excluded_dirs = %r{^(.github|dev|npm|spec)/}
37
+ excluded_files = %w[.gitignore .rspec Gemfile Gemfile.lock Rakefile package.json yarn.lock]
23
38
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
24
- f.match(%r{^(config|gemfiles|npm|spec)/}) ||
25
- %w[.gitignore .rspec .travis.yml].include?(f) ||
26
- %w[Gemfile Gemfile.lock package.json yarn.lock].include?(f)
39
+ f.match(excluded_dirs) || excluded_files.include?(f)
27
40
  end
28
- spec.required_ruby_version = '>= 2.5'
41
+ spec.required_ruby_version = '>= 2.6'
29
42
 
30
- spec.add_dependency 'rails', '>= 5.0', '<= 7.1'
43
+ spec.add_dependency 'rails', '>= 6.0', '<= 7.1'
31
44
 
32
45
  spec.add_development_dependency 'bundler', '~> 2.1'
33
- spec.add_development_dependency 'jasmine', '~> 3.2'
34
- spec.add_development_dependency 'rake'
35
- spec.add_development_dependency 'rspec', '~> 3.10.0'
36
- spec.add_development_dependency 'rspec-rails', '~> 5.0.0'
37
- spec.add_development_dependency 'rubocop'
38
- spec.add_development_dependency 'rubocop-performance'
39
- spec.add_development_dependency 'rubocop-rails'
40
- spec.add_development_dependency 'rubocop-rake'
41
- spec.add_development_dependency 'rubocop-rspec'
46
+ spec.add_development_dependency 'formtastic', '~> 4.0'
47
+ spec.add_development_dependency 'rake', '~> 13.0'
48
+ spec.add_development_dependency 'rspec', '~> 3.11'
49
+ spec.add_development_dependency 'rspec-rails', '>= 5.0'
50
+ spec.add_development_dependency 'simple_form', '~> 5.1'
42
51
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Association
5
+ class Builder # :nodoc:
6
+ attr_reader :association, :record
7
+
8
+ def initialize(record, association, options = {})
9
+ @record = record
10
+ @association = association
11
+ @options = options.dup.symbolize_keys.reverse_merge(force_non_association_create: false, wrap_object: false)
12
+ end
13
+
14
+ def build
15
+ model = reflection ? build_with_reflection : build_without_reflection
16
+ model = options[:wrap_object].call(model) if options[:wrap_object].respond_to?(:call)
17
+ model
18
+ end
19
+
20
+ protected
21
+
22
+ attr_reader :options
23
+
24
+ def model
25
+ record.class
26
+ end
27
+
28
+ def reflection
29
+ @reflection ||= model.try(:reflect_on_association, association)
30
+ end
31
+
32
+ def build_with_reflection
33
+ return build_with_conditions if should_use_conditions?
34
+
35
+ # Assume ActiveRecord or compatible
36
+ # We use a clone of the current form object to not link
37
+ # object together (even if unsaved)
38
+ dummy = record.dup
39
+ model = if reflection.collection?
40
+ dummy.send(association).build
41
+ else
42
+ dummy.send("build_#{association}")
43
+ end
44
+ model = model.dup if model.frozen?
45
+ model
46
+ end
47
+
48
+ def build_without_reflection
49
+ methods = %W[build_#{association.to_s.pluralize} build_#{association.to_s.singularize}]
50
+ available_methods = methods.select { |m| record.respond_to?(m) }
51
+ raise "Association #{association} doesn't exist on #{model}" unless available_methods.any?
52
+
53
+ record.send(available_methods.first)
54
+ end
55
+
56
+ def should_use_conditions?
57
+ reflection.class.name.starts_with?('Mongoid::') || options[:force_non_association_create]
58
+ end
59
+
60
+ def build_with_conditions
61
+ conditions = reflection.respond_to?(:conditions) ? reflection.conditions.flatten : []
62
+ reflection.klass.new(*conditions)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Association
5
+ class Renderer # :nodoc:
6
+ def initialize(template, form, association, object, options = {})
7
+ @template = template
8
+ @form = form
9
+ @association = association
10
+ @object = object
11
+ @options = options.dup.symbolize_keys
12
+ end
13
+
14
+ def render
15
+ form.public_send(form_method, association, object, form_options) do |form|
16
+ template.render(partial, **render_options(form))
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ attr_reader :template, :form, :association, :object, :options
23
+
24
+ def singular_association
25
+ association.to_s.singularize
26
+ end
27
+
28
+ def form_method
29
+ ancestors = form.class.ancestors.map(&:to_s)
30
+ return :simple_fields_for if ancestors.include?('SimpleForm::FormBuilder')
31
+ return :semantic_fields_for if ancestors.include?('Formtastic::FormBuilder')
32
+
33
+ :fields_for
34
+ end
35
+
36
+ def form_options
37
+ options.fetch(:form_options, {}).symbolize_keys.reverse_merge(child_index: "new_#{association}")
38
+ end
39
+
40
+ def partial
41
+ options.fetch(:partial, "#{singular_association}_fields")
42
+ end
43
+
44
+ def render_options(form)
45
+ options.fetch(:locals, {}).merge(form_name => form)
46
+ end
47
+
48
+ def form_name
49
+ options.fetch(:form_name, :f).to_sym
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Association # :nodoc:
5
+ autoload :Builder, 'cocooned/association/builder'
6
+ autoload :Renderer, 'cocooned/association/renderer'
7
+ end
8
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/deprecation'
4
+
5
+ module Cocooned
6
+ # Custom deprecator to use with ActiveSupport::Deprecation methods
7
+ class Deprecation < ActiveSupport::Deprecation
8
+ @deprecators = {}
9
+
10
+ def self.[](deprecation_horizon = nil)
11
+ @deprecators[deprecation_horizon] ||= new(deprecation_horizon)
12
+ end
13
+
14
+ def initialize(deprecation_horizon = nil, gem_name = 'Cocooned')
15
+ deprecation_horizon ||= format('%<major>d.0', major: Gem::Version.new(Cocooned::VERSION).segments.first + 1)
16
+ super(deprecation_horizon, gem_name)
17
+ end
18
+ end
19
+
20
+ # Deprecated methods
21
+ module Deprecated # :nodoc:
22
+ module Helpers # :nodoc:
23
+ module Tags # :nodoc:
24
+ # @deprecated: Please use {#cocooned_add_item_link} instead
25
+ def link_to_add_association(*args, &block)
26
+ cocooned_add_item_link(*args, &block)
27
+ end
28
+ deprecate link_to_add_association: 'Use :cocooned_add_link instead',
29
+ deprecator: Deprecation['3.0']
30
+
31
+ # @deprecated: Please use {#cocooned_remove_item_link} instead
32
+ def link_to_remove_association(*args, &block)
33
+ cocooned_remove_item_link(*args, &block)
34
+ end
35
+ deprecate link_to_remove_association: 'Use :cocooned_remove_item_link instead',
36
+ deprecator: Deprecation['3.0']
37
+ end
38
+ end
39
+
40
+ module TagsHelper # :nodoc:
41
+ module DefaultLabel # :nodoc:
42
+ protected
43
+
44
+ def i18n_namespaces
45
+ return super unless I18n.exists?(:cocoon)
46
+
47
+ Deprecation['3.0'].warn 'Support for the :cocoon i18n namespace will be removed in 3.0', caller_locations(3)
48
+ super + %w[cocoon]
49
+ end
50
+ end
51
+
52
+ module DataAttributes # :nodoc:
53
+ protected
54
+
55
+ # Compatibility with the old way to pass data attributes to Rails view helpers
56
+ # Has we use the :data key (introduced in Rails 3.1), they will not be looked up.
57
+ def html_data
58
+ return super unless data_keys.size.positive?
59
+
60
+ Deprecation['3.0'].warn 'Compatibility with options named data-* will be removed in 3.0', caller_locations(3)
61
+ html_data_normalize super.merge(data_options)
62
+ end
63
+
64
+ def data_keys
65
+ options.keys.select { |k| k.to_s.match?(/data[_-]/) }
66
+ end
67
+
68
+ def data_options
69
+ data_keys.each_with_object({}) do |original_key, extracted|
70
+ key = original_key.to_s.gsub(/^data[_-]/, '')
71
+ extracted[key] = options.delete(original_key)
72
+ end
73
+ end
74
+ end
75
+
76
+ module AssociationOptions # :nodoc:
77
+ protected
78
+
79
+ def association_options
80
+ if options.key? :insertion_traversal
81
+ Deprecation['3.0'].warn 'Support for the :insertion_traversal will be removed in 3.0', caller_locations(3)
82
+ end
83
+
84
+ super
85
+ end
86
+ end
87
+
88
+ module Renderer # :nodoc:
89
+ protected
90
+
91
+ def renderer_options
92
+ return super unless options.key?(:render_options)
93
+
94
+ Deprecation['3.0'].warn 'Support for :render_options will be removed in 3.0', caller_locations(3)
95
+ legacy_options = options.delete(:render_options)
96
+
97
+ super.tap do |opts|
98
+ opts[:locals] = legacy_options.delete(:locals) if legacy_options.key?(:locals)
99
+ opts[:form_options] = legacy_options
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Helpers
5
+ # Cocooned containers helpers output container or item wrappers as expected by
6
+ # the JavaScript companion package.
7
+ module Containers
8
+ # Wrap content with the expected markup for a Cocooned container.
9
+ #
10
+ # This is a wrapper around `ActionView::Base#content_tag` to automatically
11
+ # add default classes and data-attributes that define a Cocooned
12
+ # container.
13
+ #
14
+ # = Signatures
15
+ #
16
+ # cocooned_container(*arguments, **options) do
17
+ # # Container content
18
+ # end
19
+ #
20
+ # = Parameters
21
+ #
22
+ # `cocooned_container` supports following options:
23
+ #
24
+ # - `:limit` [Integer]
25
+ # Enable the limit plugin and set the accepted maximum number of items
26
+ # - `:reorderable` [Boolean|Hash]
27
+ # Enable the reorderable plugin. When a boolean, use plugin's default.
28
+ # You can also pass a Hash with explicit options (ex: `{ startAt: 1 }`).
29
+ #
30
+ # Any other argument or option supported by `ActionView::Base#content_tag`
31
+ # will be forwarded.
32
+ def cocooned_container(*args, &block)
33
+ options = args.extract_options!.dup
34
+ defaults = cocooned_wrapper_defaults(options, %w[cocooned-container], :'cocooned-container')
35
+ defaults[:data][:cocooned_options] = options.extract!(:limit, :reorderable).to_json
36
+
37
+ content_tag(:div, *args, **options.deep_merge(defaults), &block)
38
+ end
39
+
40
+ # Wrap content with the expected markup for a Cocooned item.
41
+ #
42
+ # This is a wrapper around `ActionView::Base#content_tag` to automatically
43
+ # add default classes and data-attributes that define a Cocooned item.
44
+ #
45
+ # = Signatures
46
+ #
47
+ # cocooned_item(*arguments, **options) do
48
+ # # Item content
49
+ # end
50
+ #
51
+ # = Parameters
52
+ #
53
+ # Any argument or option supported by `ActionView::Base#content_tag` will
54
+ # be forwarded.
55
+ def cocooned_item(*args, &block)
56
+ options = args.extract_options!.dup
57
+ defaults = cocooned_wrapper_defaults(options, %w[cocooned-item nested-fields], :'cocooned-item')
58
+
59
+ content_tag(:div, *args, **options.deep_merge(defaults), &block)
60
+ end
61
+
62
+ protected
63
+
64
+ def cocooned_wrapper_defaults(options, additional_classes, mark)
65
+ # TODO: Replace with compact_blank when dropping support for Rails 6.0
66
+ classes = Array.wrap(options.delete(:class)).flat_map { |k| k.to_s.split }.reject(&:blank?)
67
+
68
+ { class: (classes + additional_classes), data: { mark => true } }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Helpers
5
+ module Tags
6
+ # Helpers to generate action triggers to add items in a nested form.
7
+ #
8
+ # = Signatures
9
+ #
10
+ # {method}(label, form, association, options = {})
11
+ # # Explicit name
12
+ #
13
+ # {method}(form, association, options = {}) do
14
+ # # Name as a block
15
+ # end
16
+ #
17
+ # {method}(form, association, options = {})
18
+ # # Use default name
19
+ #
20
+ # = Parameters
21
+ #
22
+ # `label` is the text to be used as the link label. See the main
23
+ # documentation for Cocooned::Helpers::Tags for more about labelling.
24
+ #
25
+ # `form` is your form builder. Can be a SimpleForm::Builder,
26
+ # Formtastic::Builder or a standard Rails FormBuilder.
27
+ #
28
+ # `association` is the name of the nested association.
29
+ # Ex: cocooned_add_item_link "Add an item", form, :items
30
+ #
31
+ # = Options
32
+ #
33
+ # `options` can be any of the following.
34
+ #
35
+ # Association options:
36
+ #
37
+ # - **insertion_method** : the method to be used to insert new items.
38
+ # Can be any of `before`, `after`, `append`, `prepend`, `replaceWith`.
39
+ # Defaults to `before`
40
+ # - **insertion_node** : a CSS selector to match new items insertion node.
41
+ # Can be any CSS selector supported by `document.querySelector`.
42
+ # For compatibility with the original Cocoon:
43
+ # * 'this' is supported as a special value to use the trigger itself as
44
+ # insertion node.
45
+ # * If no value is specified, the trigger's parent will be used.
46
+ # - **count**: how many item will be inserted on click.
47
+ # Defaults to 1.
48
+ #
49
+ # Rendering options:
50
+ #
51
+ # - **partial**: the nested form partial.
52
+ # Defaults to `{association.singular_name}_fields`.
53
+ # - **form_name**: name used to access the form builder in the nested form
54
+ # partial. Defaults to `:f`.
55
+ # - **form_options**: options to be passed to the nested form builder. Can
56
+ # be used to specify a wrapper for simple_form_fields if you use its
57
+ # Bootstrap setup, for example. No defaults.
58
+ # - **locals**: a hash of local variables to forward to the partial. No
59
+ # default.
60
+ #
61
+ # Building options:
62
+ #
63
+ # - **wrap_object**: anything responding to `call` to be used to wrap the
64
+ # newly build item instance. Can be useful with decorators or special
65
+ # initialisations. No default.
66
+ # Ex:
67
+ # cocooned_add_item_link "Add an item", form, :items,
68
+ # wrap_object: Proc.new { |comment| CommentDecorator.new(comment) })
69
+ # - **force_non_association_create**: force to build instances of the
70
+ # nested model without calling association build methods
71
+ # (`build_{association}` or `{association}.build`). Can be usefull if,
72
+ # for some specific reason, you need an object to _not_ be created on
73
+ # the association, for example if you did not want `after_add` callbacks
74
+ # to be triggered. Defaults to false.
75
+ #
76
+ # Compatibility options:
77
+ #
78
+ # These options are supported for backward compatibility with the original
79
+ # Cocoon. **Support for these options will be removed in the next major
80
+ # release !**.
81
+ #
82
+ # - **insertion_traversal**: a jQuery DOM traversal method name to use in
83
+ # combination with **insertion_node** to find the insertion node from
84
+ # the trigger. Can be any of `closest`, `parent`, `prev`, `next` or
85
+ # `siblings`.
86
+ # Expressed in jQuery pseudo-code, the insertion node will be determined
87
+ # as the result of `$(trigger).{traversal}({insertion_node})`.
88
+ # - **render_options**: A nested Hash originally used to pass locals and form
89
+ # builder options.
90
+ module Add
91
+ # Output a link to add an item in a nested form.
92
+ #
93
+ # = Signatures
94
+ #
95
+ # cocooned_add_item_link(label, form, association, options = {})
96
+ # # Explicit name
97
+ #
98
+ # cocooned_add_item_link(form, association, options = {}) do
99
+ # # Name as a block
100
+ # end
101
+ #
102
+ # cocooned_add_item_link(form, association, options = {})
103
+ # # Use default name
104
+ #
105
+ # See Cocooned::Helpers::Tags::Add main documentation for a reference of
106
+ # supported parameters.
107
+ #
108
+ # See the documentation of +ActionView::Base#link_to+ for additional
109
+ # options.
110
+ def cocooned_add_item_link(*args, &block)
111
+ cocooned_link(Cocooned::Tags::Add, *args, &block)
112
+ end
113
+
114
+ # Output a button to add an item in a nested form.
115
+ #
116
+ # = Signatures
117
+ #
118
+ # cocooned_add_item_button(label, form, association, options = {})
119
+ # # Explicit name
120
+ #
121
+ # cocooned_add_item_button(form, association, options = {}) do
122
+ # # Name as a block
123
+ # end
124
+ #
125
+ # cocooned_add_item_button(form, association, options = {})
126
+ # # Use default name
127
+ #
128
+ # See Cocooned::Helpers::Tags::Add main documentation for a reference of
129
+ # supported options.
130
+ def cocooned_add_item_button(*args, &block)
131
+ cocooned_button(Cocooned::Tags::Add, *args, &block)
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Helpers
5
+ module Tags
6
+ # Helpers to generate action triggers to move items down in a nested form.
7
+ #
8
+ # = Signatures
9
+ #
10
+ # {method}(label, form, options = {})
11
+ # # Explicit name
12
+ #
13
+ # {method}(form, options = {}) do
14
+ # # Name as a block
15
+ # end
16
+ #
17
+ # {method}(form, options = {})
18
+ # # Use default name
19
+ #
20
+ # = Parameters
21
+ #
22
+ # `label` is the text to be used as the link label. See the main
23
+ # documentation for Cocooned::Helpers::Tags for more about labelling.
24
+ #
25
+ # `form` is your form builder. Can be a SimpleForm::Builder,
26
+ # Formtastic::Builder or a standard Rails FormBuilder.
27
+ module Down
28
+ # Output a link to move an item down.
29
+ #
30
+ # = Signatures
31
+ #
32
+ # cocooned_move_item_down_link(label, form, options = {})
33
+ # # Explicit name
34
+ #
35
+ # cocooned_move_item_down_link(form, options = {}) do
36
+ # # Name as a block
37
+ # end
38
+ #
39
+ # cocooned_move_item_down_link(form, options = {})
40
+ # # Use default name
41
+ #
42
+ # See Cocooned::Helpers::Tags::Down main documentation for a reference
43
+ # of supported parameters.
44
+ #
45
+ # See the documentation of +ActionView::Base#link_to+ for additional
46
+ # options.
47
+ def cocooned_move_item_down_link(*args, &block)
48
+ cocooned_link(Cocooned::Tags::Down, *args, &block)
49
+ end
50
+
51
+ # Output a button to move an item down.
52
+ #
53
+ # = Signatures
54
+ #
55
+ # cocooned_move_item_down_button(label, form, options = {})
56
+ # # Explicit name
57
+ #
58
+ # cocooned_move_item_down_button(form, options = {}) do
59
+ # # Name as a block
60
+ # end
61
+ #
62
+ # cocooned_move_item_down_button(form, options = {})
63
+ # # Use default name
64
+ #
65
+ # See Cocooned::Helpers::Tags::Add main documentation for a reference of
66
+ # supported parameters.
67
+ #
68
+ # See the documentation of +ActionView::Helpers::FormBuilder#button+ for
69
+ # valid options.
70
+ def cocooned_move_item_down_button(*args, &block)
71
+ cocooned_button(Cocooned::Tags::Down, *args, &block)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Helpers
5
+ module Tags
6
+ # Helpers to generate action triggers to remove items in a nested form.
7
+ #
8
+ # = Signatures
9
+ #
10
+ # {method}(label, form, options = {})
11
+ # # Explicit name
12
+ #
13
+ # {method}(form, options = {}) do
14
+ # # Name as a block
15
+ # end
16
+ #
17
+ # {method}(form, options = {})
18
+ # # Use default name
19
+ #
20
+ # = Parameters
21
+ #
22
+ # `label` is the text to be used as the link label. See the main
23
+ # documentation for Cocooned::Helpers::Tags for more about labelling.
24
+ #
25
+ # `form` is your form builder. Can be a SimpleForm::Builder,
26
+ # Formtastic::Builder or a standard Rails FormBuilder.
27
+ module Remove
28
+ # Output a link to remove an item (and an hidden field to mark
29
+ # it as destroyed if it has already been persisted).
30
+ #
31
+ # = Signatures
32
+ #
33
+ # cocooned_remove_item_link(label, form, options = {})
34
+ # # Explicit name
35
+ #
36
+ # cocooned_remove_item_link(form, options = {}) do
37
+ # # Name as a block
38
+ # end
39
+ #
40
+ # cocooned_remove_item_link(form, options = {})
41
+ # # Use default name
42
+ #
43
+ # See Cocooned::Helpers::Tags::Remove main documentation for a reference
44
+ # of supported parameters.
45
+ #
46
+ # See the documentation of +ActionView::Base#link_to+ for additional
47
+ # options.
48
+ def cocooned_remove_item_link(*args, &block)
49
+ cocooned_link(Cocooned::Tags::Remove, *args, &block)
50
+ end
51
+
52
+ # Output a button to remove an item (and an hidden field to mark
53
+ # it as destroyed if it has already been persisted).
54
+ #
55
+ # = Signatures
56
+ #
57
+ # cocooned_remove_item_link(label, form, options = {})
58
+ # # Explicit name
59
+ #
60
+ # cocooned_remove_item_link(form, options = {}) do
61
+ # # Name as a block
62
+ # end
63
+ #
64
+ # cocooned_remove_item_link(form, options = {})
65
+ # # Use default name
66
+ #
67
+ # See Cocooned::Helpers::Tags::Remove main documentation for a reference
68
+ # of supported parameters.
69
+ #
70
+ # See the documentation of +ActionView::Base#link_to+ for additional
71
+ # options.
72
+ def cocooned_remove_item_button(*args, &block)
73
+ cocooned_button(Cocooned::Tags::Remove, *args, &block)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end