cocooned 1.4.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Helpers
5
+ module Tags
6
+ # Output an action link to move an item up.
7
+ #
8
+ # = Signatures
9
+ #
10
+ # cocooned_move_item_up_link(label, form, options = {})
11
+ # # Explicit name
12
+ #
13
+ # cocooned_move_item_up_link(form, options = {}) do
14
+ # # Name as a block
15
+ # end
16
+ #
17
+ # cocooned_move_item_up_link(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 Up
28
+ # Output a link to move an item up.
29
+ #
30
+ # = Signatures
31
+ #
32
+ # cocooned_move_item_up_link(label, form, options = {})
33
+ # # Explicit name
34
+ #
35
+ # cocooned_move_item_up_link(form, options = {}) do
36
+ # # Name as a block
37
+ # end
38
+ #
39
+ # cocooned_move_item_up_link(form, options = {})
40
+ # # Use default name
41
+ #
42
+ # See Cocooned::Helpers::Tags::Up main documentation for a reference of
43
+ # supported parameters.
44
+ #
45
+ # See the documentation of +ActionView::Base#link_to+ for additional
46
+ # options.
47
+ def cocooned_move_item_up_link(*args, &block)
48
+ cocooned_link(Cocooned::Tags::Up, *args, &block)
49
+ end
50
+
51
+ # Output a button to move an item up.
52
+ #
53
+ # = Signatures
54
+ #
55
+ # cocooned_move_item_up_button(label, form, options = {})
56
+ # # Explicit name
57
+ #
58
+ # cocooned_move_item_up_button(form, options = {}) do
59
+ # # Name as a block
60
+ # end
61
+ #
62
+ # cocooned_move_item_up_button(form, options = {})
63
+ # # Use default name
64
+ #
65
+ # See Cocooned::Helpers::Tags::Up 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_up_button(*args, &block)
71
+ cocooned_button(Cocooned::Tags::Up, *args, &block)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Helpers
5
+ # Cocooned tags helpers output action link to interact with a dynamic nested form.
6
+ #
7
+ # Each action has its dedicated helpers:
8
+ #
9
+ # - `cocooned_add_item_link` / `cocooned_add_item_button` to add an item
10
+ # - `cocooned_remove_item_link` / `cocooned_remove_item_button` to remove
11
+ # an item
12
+ # - `cocooned_move_item_up_link` / `cocooned_move_item_up_button` to move
13
+ # an item up (in a reorderable form)
14
+ # - `cocooned_move_item_down_link` / `cocooned_move_item_down_link` to move
15
+ # an item down (in a reorderable form)
16
+ #
17
+ # Labelling action links
18
+ #
19
+ # The label of any action links can be given explicitly as helper's first
20
+ # argument or as a block, just as you can do on ActionView's `link_to` or
21
+ # similar helpers.
22
+ #
23
+ # Additionally, Cocooned helpers will lookup I18n translations for a default
24
+ # label based on the action name (`add`, `remove`, `up`, `down`) and the
25
+ # association name.
26
+ #
27
+ # For `add` action links, the association name used is the same as passed as
28
+ # argument. On other action triggers, it is extracted from nested form
29
+ # #object_name.
30
+ #
31
+ # You can declare default labels in your translation files with following
32
+ # keys:
33
+ #
34
+ # - `cocooned.{association}.{action}` (Ex: `cocooned.items.add`)
35
+ # - `cocooned.defaults.{action}`
36
+ #
37
+ # If no translation is found, the default label will be the humanized action
38
+ # name.
39
+ module Tags
40
+ autoload :Add, 'cocooned/helpers/tags/add'
41
+ autoload :Down, 'cocooned/helpers/tags/down'
42
+ autoload :Remove, 'cocooned/helpers/tags/remove'
43
+ autoload :Up, 'cocooned/helpers/tags/up'
44
+
45
+ include Cocooned::Deprecated::Helpers::Tags
46
+
47
+ protected
48
+
49
+ def cocooned_link(klass, *args, &block)
50
+ options = args.extract_options!
51
+ klass.create(self, *args, **options, &block).render(as: :link)
52
+ end
53
+
54
+ def cocooned_button(klass, *args, &block)
55
+ options = args.extract_options!
56
+ klass.create(self, *args, **options, &block).render(as: :button)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,334 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cocooned/helpers/deprecate'
4
- require 'cocooned/helpers/cocoon_compatibility'
5
- require 'cocooned/association_builder'
6
-
7
3
  module Cocooned
8
- # TODO: Remove in 3.0 (Only Cocoon class names).
9
- HELPER_CLASSES = {
10
- add: %w[cocooned-add add_fields],
11
- remove: %w[cocooned-remove remove_fields],
12
- up: %w[cocooned-move-up],
13
- down: %w[cocooned-move-down]
14
- }.freeze
15
-
16
- module Helpers # rubocop:disable Metrics/ModuleLength
17
- # Create aliases to old Cocoon method name
18
- # TODO: Remove in 3.0
19
- include Cocooned::Helpers::CocoonCompatibility
20
-
21
- # Output an action link to add an item in a nested form.
22
- #
23
- # ==== Signatures
24
- #
25
- # cocooned_add_item_link(label, form, association, options = {})
26
- # # Explicit name
27
- #
28
- # cocooned_add_item_link(form, association, options = {}) do
29
- # # Name as a block
30
- # end
31
- #
32
- # cocooned_add_item_link(form, association, options = {})
33
- # # Use default name
34
- #
35
- # ==== Parameters
36
- #
37
- # `label` is the text to be used as the link label.
38
- # Just as when you use the Rails builtin helper +link_to+, you can give an explicit
39
- # label to your link or use a block to build it.
40
- # If you provide neither an explicit label nor a block, Cocooned will try to use I18n
41
- # to find a suitable label. This lookup will check the following keys:
42
- #
43
- # - `cocooned.{association}.add`
44
- # - `cocooned.defaults.add`
45
- #
46
- # If none of these translation exist, it will fallback to 'Add'.
47
- #
48
- #
49
- # `form` is your form builder. Can be a SimpleForm::Builder, Formtastic::Builder
50
- # or a standard Rails FormBuilder.
51
- #
52
- # `association` is the name of the nested association.
53
- # Ex: cocooned_add_item_link "Add an item", form, :items
54
- #
55
- # ==== Options
56
- #
57
- # `options` can be any of the following.
58
- #
59
- # Rendering options:
60
- #
61
- # - **partial**: the nested form partial.
62
- # Defaults to `{association.singular_name}_fields`.
63
- # - **form_name**: name used to access the form builder in the nested form partial.
64
- # Defaults to `:f`.
65
- # - **form_options**: options to be passed to the nested form builder. Can be used
66
- # to specify a wrapper for simple_form_fields if you use its Bootstrap setup, for
67
- # example.
68
- # No defaults.
69
- # - **locals**: a hash of local variables, will be forwarded to the partial.
70
- # No default.
71
- #
72
- # Association options:
73
- #
74
- # - **insertion_method** : the jQuery method to be used to insert new items.
75
- # Can be any of `before`, `after`, `append`, `prepend`, `replaceWith`.
76
- # Defaults to `before`
77
- # - **insertion_traversal** and **insertion_node** : respectively the jQuery
78
- # traversal method and the jQuery compatible selector that will be used to find
79
- # the insertion node, relative to the generated link.
80
- # When both are specified, `$(addLink).{traversal}({node})` will be used.
81
- # When only **insertion_node** is specified, `$({node})` will be used.
82
- # When only **insertion_traversal** is specified, it will be ignored.
83
- # When none is specified, `$(addLink).parent()` will be used.
84
- # - **count**: how many item will be inserted on click.
85
- # Defaults to 1.
86
- # - **wrap_object**: anything responding to `call` to be used to wrap the newly build
87
- # item instance. Can be useful with decorators or special initialisations.
88
- # Ex: cocooned_add_item_link "Add an item", form, :items,
89
- # wrap_object: Proc.new { |comment| CommentDecorator.new(comment) })
90
- # No default.
91
- # - **force_non_association_create**: force to build instances of the nested model
92
- # outside association (i.e. without calling `build_{association}` or `{association}.build`)
93
- # Can be usefull if, for some specific reason, you need an object to _not_ be created
94
- # on the association, for example if you did not want `after_add` callbacks to be triggered.
95
- # Defaults to false.
96
- #
97
- # Link HTML options:
98
- #
99
- # You can pass any option supported by +link_to+. It will be politely forwarded.
100
- # See the documentation of +link_to+ for more informations.
101
- #
102
- # Compatibility options:
103
- #
104
- # These options are supported for backward compatibility with the original Cocoon.
105
- # **Support for these options will be removed in the next major release !**.
106
- #
107
- # - **render_options**: A nested Hash originaly used to pass locals and form builder
108
- # options.
109
- #
110
- def cocooned_add_item_link(*args, &block)
111
- if block_given?
112
- cocooned_add_item_link(capture(&block), *args)
113
-
114
- elsif args.first.respond_to?(:object)
115
- association = args.second
116
- cocooned_add_item_link(cocooned_default_label(:add, association), *args)
117
-
118
- else
119
- name, form, association, html_options = *args
120
- html_options ||= {}
121
- html_options = html_options.dup.with_indifferent_access
122
-
123
- builder_options = cocooned_extract_builder_options!(html_options)
124
- render_options = cocooned_extract_render_options!(html_options)
125
-
126
- builder = Cocooned::AssociationBuilder.new(form, association, builder_options)
127
- rendered = cocooned_render_association(builder, render_options)
128
-
129
- data = cocooned_extract_data!(html_options).merge!(
130
- association: builder.singular_name,
131
- associations: builder.plural_name,
132
- association_insertion_template: CGI.escapeHTML(rendered.to_str).html_safe
133
- )
134
-
135
- html_options[:data] = (html_options[:data] || {}).merge(data)
136
- html_options[:class] = [Array(html_options.delete(:class)).collect { |k| k.to_s.split(' ') },
137
- Cocooned::HELPER_CLASSES[:add]].flatten.compact.uniq.join(' ')
138
-
139
- link_to(name, '#', html_options)
140
- end
141
- end
142
-
143
- # Output an action link to remove an item (and an hidden field to mark
144
- # it as destroyed if it has already been persisted).
145
- #
146
- # ==== Signatures
147
- #
148
- # cocooned_remove_item_link(label, form, html_options = {})
149
- # # Explicit name
150
- #
151
- # cocooned_remove_item_link(form, html_options = {}) do
152
- # # Name as a block
153
- # end
154
- #
155
- # cocooned_remove_item_link(form, html_options = {})
156
- # # Use default name
157
- #
158
- # See the documentation of +link_to+ for valid options.
159
- def cocooned_remove_item_link(name, form = nil, html_options = {}, &block)
160
- # rubocop:disable Style/ParallelAssignment
161
- html_options, form = form, nil if form.is_a?(Hash)
162
- form, name = name, form if form.nil?
163
- # rubocop:enable Style/ParallelAssignment
164
-
165
- return cocooned_remove_item_link(capture(&block), form, html_options) if block_given?
166
-
167
- association = form.object.class.to_s.tableize
168
- return cocooned_remove_item_link(cocooned_default_label(:remove, association), form, html_options) if name.nil?
169
-
170
- link_options = html_options.dup
171
- link_options[:class] = [html_options[:class], Cocooned::HELPER_CLASSES[:remove]].flatten.compact
172
- link_options[:class] << (form.object.new_record? ? 'dynamic' : 'existing')
173
- link_options[:class] << 'destroyed' if form.object.marked_for_destruction?
174
-
175
- form.hidden_field(:_destroy, value: form.object._destroy) << link_to(name, '#', link_options)
176
- end
177
-
178
- # Output an action link to move an item up.
179
- #
180
- # ==== Signatures
181
- #
182
- # cocooned_move_item_up_link(label, form, html_options = {})
183
- # # Explicit name
184
- #
185
- # cocooned_move_item_up_link(form, html_options = {}) do
186
- # # Name as a block
187
- # end
188
- #
189
- # cocooned_move_item_up_link(form, html_options = {})
190
- # # Use default name
191
- #
192
- # See the documentation of +link_to+ for valid options.
193
- def cocooned_move_item_up_link(name, form = nil, html_options = {}, &block)
194
- cocooned_move_item_link(:up, name, form, html_options, &block)
195
- end
196
-
197
- # Output an action link to move an item down.
198
- #
199
- # ==== Signatures
200
- #
201
- # cocooned_move_item_down_link(label, html_options = {})
202
- # # Explicit name
203
- #
204
- # cocooned_move_item_down_link(html_options = {}) do
205
- # # Name as a block
206
- # end
207
- #
208
- # cocooned_move_item_down_link(html_options = {})
209
- # # Use default name
210
- #
211
- # See the documentation of +link_to+ for valid options.
212
- def cocooned_move_item_down_link(name, form = nil, html_options = {}, &block)
213
- cocooned_move_item_link(:down, name, form, html_options, &block)
214
- end
215
-
216
- private
217
-
218
- def cocooned_move_item_link(direction, name, form = nil, html_options = {}, &block)
219
- form, name = name, form if form.nil?
220
- return cocooned_move_item_link(direction, capture(&block), form, html_options) if block_given?
221
- return cocooned_move_item_link(direction, cocooned_default_label(direction), form, html_options) if name.nil?
222
-
223
- link_options = html_options.dup
224
- link_options[:class] = [html_options[:class], Cocooned::HELPER_CLASSES[direction]].flatten.compact.join(' ')
225
- link_to name, '#', link_options
226
- end
227
-
228
- def cocooned_default_label(action, association = nil)
229
- # TODO: Remove in 3.0
230
- if I18n.exists?(:cocoon)
231
- msg = Cocooned::Helpers::Deprecate.deprecate_release_message('the :cocoon i18n scope', ':cocooned')
232
- warn msg
233
- end
234
-
235
- keys = ["cocooned.defaults.#{action}", "cocoon.defaults.#{action}"]
236
- keys.unshift("cocooned.#{association}.#{action}", "cocoon.#{association}.#{action}") unless association.nil?
237
- keys.collect!(&:to_sym)
238
- keys << action.to_s.humanize
239
-
240
- I18n.translate(keys.take(1).first, default: keys.drop(1))
241
- end
242
-
243
- def cocooned_render_association(builder, options = {})
244
- render_options = options.dup
245
- locals = (render_options.delete(:locals) || {}).symbolize_keys
246
- partial = render_options.delete(:partial) || "#{builder.singular_name}_fields"
247
- form_name = render_options.delete(:form_name)
248
- form_options = (render_options.delete(:form_options) || {}).symbolize_keys
249
- form_options.reverse_merge!(child_index: "new_#{builder.association}")
250
-
251
- builder.form.send(cocooned_form_method(builder.form),
252
- builder.association,
253
- builder.build_object,
254
- form_options) do |form_builder|
255
- partial_options = { form_name.to_sym => form_builder, :dynamic => true }.merge(locals)
256
- render(partial, partial_options)
257
- end
258
- end
259
-
260
- def cocooned_form_method(form)
261
- ancestors = form.class.ancestors.map(&:to_s)
262
- if ancestors.include?('SimpleForm::FormBuilder')
263
- :simple_fields_for
264
- elsif ancestors.include?('Formtastic::FormBuilder')
265
- :semantic_fields_for
266
- else
267
- :fields_for
268
- end
269
- end
270
-
271
- def cocooned_extract_builder_options!(html_options)
272
- %i[wrap_object force_non_association_create].each_with_object({}) do |option_name, opts|
273
- opts[option_name] = html_options.delete(option_name) if html_options.key?(option_name)
274
- end
275
- end
276
-
277
- def cocooned_extract_render_options!(html_options)
278
- render_options = { form_name: :f }
279
-
280
- # TODO: Remove in 2.0
281
- if html_options.key?(:render_options)
282
- msg = Cocooned::Helpers::Deprecate.deprecate_release_message(':render_options', :none)
283
- warn msg
284
-
285
- options = html_options.delete(:render_options)
286
- render_options[:locals] = options.delete(:locals) if options.key?(:locals)
287
- render_options[:form_options] = options
288
- end
289
-
290
- %i[locals partial form_name form_options].each_with_object(render_options) do |option_name, opts|
291
- opts[option_name] = html_options.delete(option_name) if html_options.key?(option_name)
292
- end
293
- end
294
-
295
- def cocooned_extract_data!(html_options)
296
- data = {
297
- count: [
298
- html_options.delete(:count).to_i,
299
- (html_options.key?(:data) ? html_options[:data].delete(:count) : 0).to_i,
300
- 1
301
- ].compact.max,
302
- association_insertion_node: cocooned_extract_single_data!(html_options, :insertion_node),
303
- association_insertion_method: cocooned_extract_single_data!(html_options, :insertion_method),
304
- association_insertion_traversal: cocooned_extract_single_data!(html_options, :insertion_traversal)
305
- }
306
-
307
- # Compatibility with the old way to pass data attributes to Rails view helpers
308
- # Has we build a :data key, they will not be looked up.
309
- html_options.keys.select { |k| k.to_s.match?(/data[_-]/) }.each_with_object(data) do |data_key, d|
310
- key = data_key.to_s.gsub(/^data[_-]/, '')
311
- d[key] = html_options.delete(data_key)
312
- end
313
- data.compact
314
- end
315
-
316
- def cocooned_extract_single_data!(hash, key)
317
- k = key.to_s
318
- return hash.delete(k) if hash.key?(k)
319
-
320
- # Compatibility alternatives
321
- # TODO: Remove in 2.0
322
- return hash.delete("association_#{k}") if hash.key?("association_#{k}")
323
- return hash.delete("data_association_#{k}") if hash.key?("data_association_#{k}")
324
- return hash.delete("data-association-#{k.tr('_', '-')}") if hash.key?("data-association-#{k.tr('_', '-')}")
325
- return nil unless hash.key?(:data)
326
-
327
- d = hash[:data].with_indifferent_access
328
- return d.delete("association_#{k}") if d.key?("association_#{k}")
329
- return d.delete("association-#{k.tr('_', '-')}") if d.key?("association-#{k.tr('_', '-')}")
330
-
331
- nil
332
- end
4
+ module Helpers # :nodoc:
5
+ autoload :Containers, 'cocooned/helpers/containers'
6
+ autoload :Tags, 'cocooned/helpers/tags'
333
7
  end
334
8
  end
@@ -4,10 +4,15 @@ require 'rails'
4
4
  require 'cocooned/helpers'
5
5
 
6
6
  module Cocooned
7
- class Railtie < ::Rails::Engine
7
+ class Railtie < ::Rails::Engine # :nodoc:
8
8
  initializer 'cocooned.initialize' do |_app|
9
9
  ActiveSupport.on_load :action_view do
10
- ActionView::Base.include Cocooned::Helpers
10
+ include Cocooned::Helpers::Tags,
11
+ Cocooned::Helpers::Tags::Add,
12
+ Cocooned::Helpers::Tags::Down,
13
+ Cocooned::Helpers::Tags::Remove,
14
+ Cocooned::Helpers::Tags::Up,
15
+ Cocooned::Helpers::Containers
11
16
  end
12
17
  end
13
18
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Tags
5
+ class Add < Base # :nodoc:
6
+ module AssociationOptions # :nodoc:
7
+ protected
8
+
9
+ def association_options
10
+ # TODO: Replace with compact_blank when dropping support for Rails 6.0
11
+ {
12
+ association: association,
13
+ template: html_template_name,
14
+ association_insertion_count: [options.delete(:count).to_i, 1].compact.max,
15
+ association_insertion_node: options.delete(:insertion_node),
16
+ association_insertion_method: options.delete(:insertion_method),
17
+ association_insertion_traversal: options.delete(:insertion_traversal)
18
+ }.reject { |_, value| value.blank? }
19
+ end
20
+ end
21
+
22
+ include Cocooned::TagsHelper::AssociationLabel
23
+
24
+ include Cocooned::TagsHelper::Renderer
25
+ include Cocooned::Deprecated::TagsHelper::Renderer
26
+ include AssociationOptions
27
+ include Cocooned::Deprecated::TagsHelper::AssociationOptions
28
+
29
+ def initialize(template, form, association, **options, &block)
30
+ @association = association
31
+ super(template, form, **options, &block)
32
+ end
33
+
34
+ def render(as: :link)
35
+ template.safe_join([super(as: as), html_template])
36
+ end
37
+
38
+ protected
39
+
40
+ attr_reader :association
41
+
42
+ def html_template
43
+ template.content_tag(:template, data: { name: html_template_name }) do
44
+ renderer.render.html_safe # rubocop:disable Rails/OutputSafety
45
+ end
46
+ end
47
+
48
+ def html_template_name
49
+ @html_template_name ||= SecureRandom.uuid
50
+ end
51
+
52
+ def html_classes
53
+ super + %w[cocooned-add add_fields]
54
+ end
55
+
56
+ def html_data
57
+ super.merge(association_options, cocooned_trigger: :add)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Tags
5
+ class Base # :nodoc:
6
+ include Cocooned::TagsHelper::DefaultLabel
7
+ include Cocooned::Deprecated::TagsHelper::DefaultLabel
8
+
9
+ include Cocooned::TagsHelper::DataAttributes
10
+ include Cocooned::Deprecated::TagsHelper::DataAttributes
11
+
12
+ class << self
13
+ def create(template, *args, **kwargs, &block)
14
+ return new(template, *args, **kwargs, &block) if block
15
+ return new(template, *args, **kwargs) unless args.first.is_a?(String)
16
+
17
+ label = args.shift
18
+ new(template, *args, **kwargs) { label }
19
+ end
20
+ end
21
+
22
+ def initialize(template, form, **options, &block)
23
+ @template = template
24
+ @form = form
25
+ @options = options.dup.symbolize_keys
26
+ @label_block = block if block
27
+ end
28
+
29
+ def render(as: :link)
30
+ return template.link_to('#', html_options) { label } if as == :link
31
+ return template.button_tag(type: :button, **html_options) { label } if as == :button
32
+
33
+ raise ArgumentError, "Unsupported value for :as. Expected :link or :button, got #{as}."
34
+ end
35
+
36
+ protected
37
+
38
+ attr_reader :template, :form, :options, :label_block
39
+
40
+ def label
41
+ return default_label if label_block.blank?
42
+
43
+ label_block.call
44
+ end
45
+
46
+ def html_options
47
+ @html_options ||= begin
48
+ options[:class] = html_classes
49
+ options[:data] = html_data
50
+ # TODO: Replace with compact_blank when dropping support for Rails 6.0
51
+ options.reject { |_, value| value.blank? }
52
+ end
53
+ end
54
+
55
+ def html_classes
56
+ # TODO: Replace with compact_blank when dropping support for Rails 6.0
57
+ Array.wrap(options.delete(:class)).flat_map { |k| k.to_s.split }.reject(&:blank?)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Tags
5
+ class Down < Base # :nodoc:
6
+ include Cocooned::TagsHelper::AssociationLabel
7
+
8
+ protected
9
+
10
+ def html_classes
11
+ super + %w[cocooned-move-down]
12
+ end
13
+
14
+ def html_data
15
+ super.merge(cocooned_trigger: :down)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Tags
5
+ class Remove < Base # :nodoc:
6
+ include Cocooned::TagsHelper::AssociationLabel
7
+
8
+ def render(as: :link)
9
+ template.safe_join([hidden_field, super(as: as)])
10
+ end
11
+
12
+ protected
13
+
14
+ def hidden_field
15
+ form.hidden_field(:_destroy, value: marked_for_destruction?)
16
+ end
17
+
18
+ def html_classes
19
+ super + %w[cocooned-remove remove_fields]
20
+ end
21
+
22
+ def html_data
23
+ super.merge(cocooned_trigger: :remove, cocooned_persisted: persisted?)
24
+ end
25
+
26
+ def persisted?
27
+ !!form.object.try(:persisted?)
28
+ end
29
+
30
+ def marked_for_destruction?
31
+ !!form.object.try(:marked_for_destruction?)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ module Tags
5
+ class Up < Base # :nodoc:
6
+ include Cocooned::TagsHelper::AssociationLabel
7
+
8
+ protected
9
+
10
+ def html_classes
11
+ super + %w[cocooned-move-up]
12
+ end
13
+
14
+ def html_data
15
+ super.merge(cocooned_trigger: :up)
16
+ end
17
+ end
18
+ end
19
+ end