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.
data/README.md CHANGED
@@ -1,77 +1,105 @@
1
1
  # Cocooned
2
2
 
3
- [![Build Status](https://travis-ci.org/notus-sh/cocooned.png?branch=master)](https://travis-ci.org/notus-sh/cocooned)
3
+ [![Unit tests](https://github.com/notus-sh/cocooned/actions/workflows/unit-tests.yml/badge.svg?branch=main)](https://github.com/notus-sh/cocooned/actions/workflows/unit-tests.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/composite_content.svg)](https://badge.fury.io/rb/cocooned)
4
5
 
5
- Cocooned makes it easier to handle nested forms in a Rails project.
6
+ Cocooned makes it easier to handle nested forms in Rails.
6
7
 
7
- Cocooned is form builder-agnostic: it works with standard Rails (>= 5.0, < 7.0) form helpers, [Formtastic](https://github.com/justinfrench/formtastic) or [SimpleForm](https://github.com/plataformatec/simple_form).
8
+ Cocooned is form builder-agnostic: it works with standard Rails (>= 6.0, < 7.1) form helpers, [Formtastic](https://github.com/justinfrench/formtastic) or [SimpleForm](https://github.com/plataformatec/simple_form).
9
+
10
+ 1. [Background](#some-background)
11
+ 2. [Installation](#installation)
12
+ 3. [Getting started](#getting-started)
13
+ 4. [Going further with plugins](#plugins)
14
+ 5. [Links or buttons ?](#links-or-buttons)
15
+ 5. [I18n integration](#internationalisation)
16
+ 6. [JavaScript](#javascript)
17
+ 7. [Styling](#styling-forms)
18
+ 8. [Migration from a previous version](#migration-from-a-previous-version) or from Cocoon
8
19
 
9
20
  ## Some Background
10
21
 
11
22
  Cocooned is a fork of [Cocoon](https://github.com/nathanvda/cocoon) by [Nathan Van der Auwera](https://github.com/nathanvda). He and all Cocoon contributors did a great job to maintain it for years. Many thanks to them!
12
23
 
13
- However, the project seems to have only received minimal fixes since 2018 and many pull requests, even simple ones, have been on hold for a long time. In 2019, as I needed a more than what Cocoon provided at this time, I had the choice to either maintain an extension or to fork it and integrate everything that was waiting and more.
14
-
15
- Cocooned is almost a complete rewrite of Cocoon, with more functionnalities, a more fluent API (I hope) and integration with modern toolchains (including webpacker).
24
+ However, the project seems to have only received minimal fixes since 2018 and many pull requests, even simple ones, have been on hold for a long time. In 2019, as I needed more than what Cocoon provided at this time, I had the choice to either maintain an extension or to fork it and integrate everything that was waiting and more.
16
25
 
17
- **For now, Cocooned is completely compatible with Cocoon and can be used as a drop-in replacement** as long as we talk about Ruby code. Just change the name of the gem in your Gemfile and you're done. It will work the same (but will add a bunch of deprecation warning to your logs).
26
+ Over the time, Cocooned turned into an almost complete rewrite of Cocoon with more functionnalities, a more fluent API (I hope) and integration with modern toolchains. Still, **Cocooned is completely compatible with Cocoon and can be used as a drop-in replacement** as long as we talk about Ruby code. Change the name of the gem in your Gemfile and you're done. **This compatibility layer with the original Cocoon API will be dropped in the next major release.**
18
27
 
19
- **This compatibility layer with the original Cocoon API will be dropped in Cocooned 3.0.**
20
-
21
- On the JavaScript side, Cocoon 1.2.13 introduced the original browser event as a third parameter to all event handlers. Meanwhile, Cocooned already started to use this positional parameter to pass the Cocooned object instance (since 1.3.0). To get access to the original event, [you'll have to change your handlers and use `event.originalEvent`](#javascript-callbacks).
28
+ On the JavaScript side, Cocooned 2.0 removed the dependency to jQuery (Yeah! :tada:). See [JavaScript](#javascript) for details.
22
29
 
23
30
  ## Installation
24
31
 
25
- Inside your `Gemfile` add the following:
32
+ Add `cocooned` to your `Gemfile`:
26
33
 
27
34
  ```ruby
28
- gem "cocooned"
35
+ gem 'cocooned'
36
+ ```
37
+
38
+ ### Load Cocooned JavaScript
39
+
40
+ Cocooned comes with an NPM companion package: [`@notus.sh/cocooned`](https://www.npmjs.com/package/@notus.sh/cocooned).
41
+ It bundles JavaScript files to handles in-browser interactions with your nested forms.
42
+
43
+ If you use import maps (Rails 7.0+ default), add it with:
44
+
45
+ ```shell
46
+ $ bin/importmap pin @notus.sh/cocooned
47
+ ```
48
+
49
+ If you use Yarn and Webpack (Rails 5.1+ default), add it with:
50
+
51
+ ```shell
52
+ $ yarn add @notus.sh/cocooned
53
+ ```
54
+
55
+ **Note:** To ensure you will always get the version of the companion package that match with the gem version, you should specify the same version constraint you used for the `cocooned` gem in your `Gemfile`.
56
+
57
+ Once installed, load it into your application with:
58
+
59
+ ```javascript
60
+ import Cocooned from '@notus.sh/cocooned'
61
+ Cocooned.start()
29
62
  ```
30
63
 
31
- ### Load Cocooned styles and scripts
64
+ If you still use Sprockets to bundle your javascripts (Rails 3.1+ default), you can either install the companion package from npmjs.org with the package manager of your choice, configure Sprockets to look for files in your application's `/node_modules` directory and load it as above (recommended) or require `cocooned` in your `application.js` with:
32
65
 
33
- If you use Sprockets, you have to require `cocooned` in your `application.js` and `application.css`, so it compiles with the asset pipeline.
66
+ ```javascript
67
+ //= require 'cocooned'
68
+ ```
34
69
 
35
- If you use Yarn to manage your non-Ruby dependencies and/or Webpack to build your assets, you can install the [`@notus.sh/cocooned` companion package](https://www.npmjs.com/package/@notus.sh/cocooned).
70
+ **This compatibility with aging Rails assets pipelines will be removed in the next major release.**
36
71
 
37
- ## Usage
72
+ ## Getting started
38
73
 
39
74
  For all the following examples, we will consider modelisation of an administrable list with items.
40
75
  Here are the two ActiveRecord models : `List` and `Item`:
41
76
 
42
77
  ```ruby
43
- class Item < ApplicationRecord
78
+ # == Schema Info
79
+ #
80
+ # Table name: lists
81
+ #
82
+ # id :integer(11) not null, primary key
83
+ # name :string
84
+ class List < ApplicationRecord
44
85
  has_many :items, inverse_of: :list
45
86
  accepts_nested_attributes_for :items, reject_if: :all_blank, allow_destroy: true
46
87
  end
47
88
 
89
+ # == Schema Info
90
+ #
91
+ # Table name: items
92
+ #
93
+ # id :integer(11) not null, primary key
94
+ # list_id :integer(11) not null
95
+ # description :text
96
+ # done :bool not null, default(false)
48
97
  class Item < ApplicationRecord
49
98
  belongs_to :list
50
99
  end
51
100
  ```
52
101
 
53
- We will build a form where we can dynamically add and remove items to a list.
54
-
55
- ### Strong Parameters Gotcha
56
-
57
- To destroy nested models, Rails uses a virtual attribute called `_destroy`.
58
- When `_destroy` is set, the nested model will be deleted. If the record has previously been persisted, Rails generate and use an automatic `id` field to fetch the wannabe destroyed record.
59
-
60
- When using Rails > 4.0 (or strong parameters), you need to explicitly add both `:id` and `:_destroy` to the list of permitted parameters.
61
-
62
- E.g. in your `ListsController`:
63
-
64
- ```ruby
65
- def list_params
66
- params.require(:list).permit(:name, tasks_attributes: [:id, :description, :done, :_destroy])
67
- end
68
- ```
69
-
70
- ### Has One Gotcha
71
-
72
- If you have a `has_one` association, then you (probably) need to set `force_non_association_create: true` on `link_to_add_association` or the associated object will be destroyed every time the edit form is rendered (which is probably not what you expect).
73
-
74
- See the [original merge request](https://github.com/nathanvda/cocoon/pull/247) for more details.
102
+ We will build a form where we can dynamically add items to a list, remove or reorder them.
75
103
 
76
104
  ### Basic form
77
105
 
@@ -79,48 +107,54 @@ See the [original merge request](https://github.com/nathanvda/cocoon/pull/247) f
79
107
 
80
108
  ```erb
81
109
  <% # `app/views/lists/_form.html.erb` %>
82
- <%= form_for @list do |f| %>
83
- <%= f.input :name %>
110
+ <%= form_for @list do |form| %>
111
+ <%= form.text_field :name %>
84
112
 
85
113
  <h3>Items</h3>
86
- <%= f.fields_for :tasks do |item_form| %>
87
- <% # This block is repeated for every task in @list.items %>
114
+ <%= form.fields_for :items do |item_form| %>
88
115
  <%= item_form.label :description %>
89
116
  <%= item_form.text_field :description %>
90
117
  <%= item_form.check_box :done %>
91
118
  <% end %>
92
119
 
93
- <%= f.submit "Save" %>
120
+ <%= form.submit "Save" %>
94
121
  <% end %>
95
122
  ```
96
123
 
97
124
  To enable Cocooned on this form, we need to:
98
125
 
99
126
  1. Move the nested form to a partial
100
- 2. Add a way to add a new item to the collection
101
- 3. Add a way to remove an item from the collection
102
- 4. Initialize Cocooned to handle this form
127
+ 2. Signal to Cocooned it should handle your form
128
+ 3. Add a way to add a new item to the list
129
+ 4. Add a way to remove an item from the collection
103
130
 
104
131
  Let's do it.
105
132
 
106
- #### 1. Move the nested form to a partial
133
+ **Note:** In this example, we will use Cocooned helpers named with a `_link` suffix. If you want to use buttons in your forms instead, the same helpers exist with a `_button` suffix.
107
134
 
108
- We now have two files:
135
+ ### 1. Move the nested form to a partial
109
136
 
110
- ```erb
137
+ Change your main form as follow:
138
+
139
+ ```diff
111
140
  <% # `app/views/lists/_form.html.erb` %>
112
141
  <%= form_for @list do |form| %>
113
- <%= form.input :name %>
142
+ <%= form.text_field :name %>
114
143
 
115
144
  <h3>Items</h3>
116
145
  <%= form.fields_for :items do |item_form|
117
- <%= render 'item_fields', f: item_form %>
146
+ - <%= item_form.label :description %>
147
+ - <%= item_form.text_field :description %>
148
+ - <%= item_form.check_box :done %>
149
+ + <%= render 'item_fields', f: item_form %>
118
150
  <% end %>
119
151
 
120
152
  <%= form.submit "Save" %>
121
153
  <% end %>
122
154
  ```
123
155
 
156
+ And create a new file where items fields are defined:
157
+
124
158
  ```erb
125
159
  <% # `app/views/lists/_item_fields.html.erb` %>
126
160
  <%= f.label :description %>
@@ -128,196 +162,234 @@ We now have two files:
128
162
  <%= f.check_box :done %>
129
163
  ```
130
164
 
131
- #### 2. Add a way to add a new item to the collection
165
+ ### 2. Signal to Cocooned it should handle your form
132
166
 
133
- ```erb
167
+ Change your main form as follow:
168
+
169
+ ```diff
134
170
  <% # `app/views/lists/_form.html.erb` %>
135
171
  <%= form_for @list do |form| %>
136
172
  <%= form.input :name %>
137
173
 
138
174
  <h3>Items</h3>
139
- <div id="items">
140
- <%= form.fields_for :tasks do |item_form| %>
175
+ + <%= cocooned_container do %>
176
+ <%= form.fields_for :items do |item_form| %>
141
177
  <%= render 'item_fields', f: item_form %>
142
178
  <% end %>
143
-
144
- <div class="links">
145
- <%= cocooned_add_item_link 'Add an item', form, :items %>
146
- </div>
147
- </div>
179
+ + <% end %>
148
180
 
149
181
  <%= form.submit "Save" %>
150
182
  <% end %>
151
183
  ```
152
184
 
153
- By default, a new item will be inserted just before the immediate parent of the 'Add an item' link. You can have a look at the documentation of `cocooned_add_item_link` for more information about how to change that but we'll keep it simple for now.
185
+ And your sub form as follow:
154
186
 
155
- #### 3. Add a way to remove an item from the collection
156
-
157
- ```erb
187
+ ```diff
158
188
  <% # `app/views/lists/_item_fields.html.erb` %>
159
- <div class="cocooned-item">
160
- <%= f.label :description %>
161
- <%= f.text_field :description %>
162
- <%= f.check_box :done %>
163
- <%= cocooned_remove_item_link 'Remove', f %>
164
- </div>
189
+ + <%= cocooned_item do %>
190
+ <%= f.label :description %>
191
+ <%= f.text_field :description %>
192
+ <%= f.check_box :done %>
193
+ + <% end %>
165
194
  ```
166
195
 
167
- The `cocooned-item` class is required for the `cocooned_remove_item_link` to work correctly.
196
+ The `cocooned_container` and `cocooned_item` helpers will set for you the HTML attributes the JavaScript part of Cocooned expect to find to hook on. They will forward any option supported by ActionView's `content_tag`.
168
197
 
169
- #### 4. Initialize Cocooned to handle this form
198
+ ### 3. Add a way to add a new item to the list
170
199
 
171
- Cocooned will detect on page load forms it should handle and initialize itself.
172
- This detection is based on the presence of a `data-cocooned-options` attribute on the nested forms container.
200
+ Change your main form as follow:
173
201
 
174
- ```erb
202
+ ```diff
175
203
  <% # `app/views/lists/_form.html.erb` %>
176
204
  <%= form_for @list do |form| %>
177
205
  <%= form.input :name %>
178
206
 
179
207
  <h3>Items</h3>
180
- <div id="items" data-cocooned-options="<%= {}.to_json %>">
181
- <%= form.fields_for :tasks do |item_form| %>
208
+ <%= cocooned_container do %>
209
+ <%= form.fields_for :items do |item_form| %>
182
210
  <%= render 'item_fields', f: item_form %>
183
211
  <% end %>
184
-
185
- <div class="links">
186
- <%= cocooned_add_item_link 'Add an item', form, :items %>
187
- </div>
188
- </div>
212
+ +
213
+ + <p><%= cocooned_add_item_link 'Add an item', form, :items %></p>
214
+ <% end %>
189
215
 
190
216
  <%= form.submit "Save" %>
191
- </div>
192
- ```
217
+ <% end %>
218
+ ```
193
219
 
194
- You're done!
220
+ By default, new items will be inserted just before the immediate parent of the 'Add an item' link. You can have a look at the documentation of `cocooned_add_item_link` for more information about how to change that but we'll keep it simple for now.
195
221
 
196
- ### Wait, what's the point of `data-cocooned-options` if it's to be empty?
222
+ ### 4. Add a way to remove an item from the collection
197
223
 
198
- For simple use cases as the one we just demonstrated, the `data-cocooned-options` attributes only triggers the Cocooned initialization on page load. But you can use it to pass additional options to the Cocooned javascript and enable plugins.
224
+ Change your sub form as follow:
199
225
 
200
- For now, Cocooned supports two plugins:
226
+ ```diff
227
+ <% # `app/views/lists/_item_fields.html.erb` %>
228
+ <%= cocooned_item do %>
229
+ <%= f.label :description %>
230
+ <%= f.text_field :description %>
231
+ <%= f.check_box :done %>
232
+ + <%= cocooned_remove_item_link 'Remove', f %>
233
+ <% end %>
234
+ ```
201
235
 
202
- * **Limit**, to set a maximum limit of items that can be added to the association
203
- * **Reorderable**, that will automatically update `position` fields when you add or remove an item or when you reorder associated items.
236
+ You're done!
204
237
 
205
- #### The limit plugin
238
+ ### Gotchas
206
239
 
207
- The limit plugin is autoloaded when needed and does not require anything more than you specifiying the maximum number of items allowed in the association.
240
+ #### Strong Parameters Gotcha
208
241
 
209
- ```erb
210
- <% # `app/views/lists/_form.html.erb` %>
211
- <%= form_for @list do |form| %>
212
- <%= form.input :name %>
213
-
214
- <h3>Items</h3>
215
- <div id="items" data-cocooned-options="<%= { limit: 12 }.to_json %>">
216
- <%= form.fields_for :tasks do |item_form| %>
217
- <%= render 'item_fields', f: item_form %>
218
- <% end %>
219
-
220
- <div class="links">
221
- <%= cocooned_add_item_link 'Add an item', form, :items %>
222
- </div>
223
- </div>
224
-
225
- <%= form.submit "Save" %>
226
- <% end %>
242
+ To destroy nested models, Rails uses a virtual attribute called `_destroy`. When `_destroy` is set, the nested model will be deleted. If a record has previously been persisted, Rails generates and uses an additional `id` field.
243
+
244
+ When using Rails > 4.0 (or strong parameters), you need to explicitly add both `:id` and `:_destroy` to the list of permitted parameters in your controller.
245
+
246
+ In our example:
247
+
248
+ ```ruby
249
+ def list_params
250
+ params.require(:list).permit(:name, tasks_attributes: [:id, :description, :done, :_destroy])
251
+ end
227
252
  ```
228
253
 
229
- #### The reorderable plugin
254
+ #### Has One Gotcha
255
+
256
+ If you have a `has_one` association, then you (probably) need to set `force_non_association_create: true` on `cocooned_add_item_link` or the associated object will be destroyed every time the edit form is rendered (which is probably not what you expect).
257
+
258
+ See the [original merge request](https://github.com/nathanvda/cocoon/pull/247) for more details.
230
259
 
231
- The reorderable plugin is autoloaded when activated and does not support any particular options.
260
+ ## Plugins
261
+
262
+ Cocooned comes with two built-in plugins:
263
+
264
+ * **Limit**, to set a maximum limit of items the association can contain
265
+ * **Reorderable**, that will automatically update a `position` field in each of your sub forms when you add, remove or move an item.
266
+
267
+ ### The limit plugin
268
+
269
+ The limit plugin requires you specify the maximum number of items allowed in the association. To do so, pass a `:limit` option to the `cocooned_container` helper:
232
270
 
233
271
  ```erb
234
- <% # `app/views/lists/_form.html.haml` %>
235
- <%= form_for @list do |form| %>
236
- <%= form.input :name %>
237
-
238
- <h3>Items</h3>
239
- <div id="items" data-cocooned-options="<%= { reorderable: true }.to_json %>">
240
- <%= form.fields_for :tasks do |item_form| %>
241
- <%= render 'item_fields', f: item_form %>
242
- <% end %>
243
-
244
- <div class="links">
245
- <%= cocooned_add_item_link 'Add an item', form, :items %>
246
- </div>
247
- </div>
248
-
249
- <%= form.submit "Save" %>
272
+ <%= cocooned_container limit: 12 do %>
273
+ <% # […] %>
250
274
  <% end %>
251
275
  ```
252
276
 
253
- However, you need to edit your nested partial to add the links that allow your users to move an item up or down in the collection and to add a `position` field.
277
+ ### The reorderable plugin
254
278
 
255
- ```erb
279
+ **Important:** To use the reorderable plugin, your model must have a `position` numeric attribute you use to order collections (as in [acts_as_list](https://rubygems.org/gems/acts_as_list)).
280
+
281
+ The reorderable plugin can be activated in two ways through the `cocooned_container` helper:
282
+
283
+ - With a boolean: `cocooned_container reorderable: true`
284
+ Will use plugin's defaults (and start counting positions at 1)
285
+ - With a configuration hash: `cocooned_container reorderable: { startAt: 0 }`
286
+ Will use given `:startAt` as base position
287
+
288
+ To be able to move items up and down in your form and for positions to be saved, you need to change your sub form as follow:
289
+
290
+ ```diff
256
291
  <% # `app/views/lists/_item_fields.html.erb` %>
257
- <div class="cocooned-item">
292
+ <%= cocooned_item do %>
258
293
  <%= f.label :description %>
259
294
  <%= f.text_field :description %>
260
295
  <%= f.check_box :done %>
261
- <%= f.hidden_field :position %>
262
- <%= cocooned_move_item_up_link 'Up', f %>
263
- <%= cocooned_move_item_down_link 'Down', f %>
296
+ + <%= f.hidden_field :position %>
297
+ + <%= cocooned_move_item_up_link 'Up', f %>
298
+ + <%= cocooned_move_item_down_link 'Down', f %>
264
299
  <%= cocooned_remove_item_link 'Remove', f %>
265
- </div>
300
+ <% end %>
266
301
  ```
267
302
 
268
- Also, remember the strong parameters gotcha we mentioned earlier.
303
+ Remember to add `:position` as a permitted parameter in your controller.
269
304
 
270
- Of course, it means your model must have a `position` attribute you will use to sort collections.
305
+ ## Links or buttons?
271
306
 
272
- ## How it works
307
+ Each helper provided by Cocooned with a name ending with `_link` has its `_button` equivalent, to generate a `<button type="button" />` instead of a `<a href="#" />`:
273
308
 
274
- Cocooned defines some helper functions:
309
+ - `cocooned_add_item_link` <=> `cocooned_add_item_button` ([Documentation](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers/tags/add.rb))
310
+ - `cocooned_remove_item_link` <=> `cocooned_remove_item_button` ([Documentation](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers/tags/remove.rb))
311
+ - `cocooned_move_item_up_link` <=> `cocooned_move_item_up_button` ([Documentation](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers/tags/up.rb))
312
+ - `cocooned_move_item_down_link` <=> `cocooned_move_item_down_button` ([Documentation](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers/tags/down.rb))
275
313
 
276
- * `cocooned_add_item_link` will build a link that, when clicked, dynamically adds a new partial form for the given association. [Have a look at the documentation for available options](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers.rb#L21).
277
- * `cocooned_remove_item_link` will build a link that, when clicked, dynamically removes the surrounding partial form. [Have a look at the documentation for available options](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers.rb#L143).
278
- * `cocooned_move_item_up_link` and `cocooned_move_item_down_link` will build links that, when clicked, will move the surrounding partial form one step up or down in the collection. [Have a look at the documentation for available options](https://github.com/notus-sh/cocooned/blob/master/lib/cocooned/helpers.rb#L178).
314
+ While all `_link` helpers accept and will politely forward any option supported by ActionView's `link_to`, `_button` helpers will do the same with options supported by ActionView's `button_tag`.
279
315
 
280
- ### Javascript callbacks
316
+ ## Internationalisation
281
317
 
282
- When your collection is modified, the following events can be triggered:
318
+ The label of any action trigger can be given explicitly as helper's first argument or as a block, just as you can do with ActionView's `link_to` or `button_to`.
283
319
 
284
- * `cocooned:before-insert`: called before inserting a new nested child, can be [canceled](#canceling-an-action)
285
- * `cocooned:after-insert`: called after inserting
286
- * `cocooned:before-remove`: called before removing the nested child, can be [canceled](#canceling-an-action)
287
- * `cocooned:after-remove`: called after removal
320
+ Additionally, Cocooned helpers will lookup I18n translations for a default label based on the action name (`add`, `remove`, `up`, `down`) and the association name. For `add` triggers, the association name used is the same as passed as argument. Other triggers extract the association name from form's `#object_name`.
288
321
 
289
- The limit plugin can trigger its own event:
322
+ You can declare default labels in your translation files with following keys:
290
323
 
291
- * `cocooned:limit-reached`: called when the limit is reached (before a new item will be inserted)
324
+ - `cocooned.{association}.{action}` (Ex: `cocooned.items.add`)
325
+ - `cocooned.defaults.{action}`
292
326
 
293
- And so does the reorderable plugin:
327
+ If no translation is found, the default label will be the humanized action name.
294
328
 
295
- * `cocooned:before-move`: called before moving the nested child, can be [canceled](#canceling-an-action)
296
- * `cocooned:after-move`: called after moving
297
- * `cocooned:before-reindex`: called before updating the `position` fields of nested items, can be [canceled](#canceling-an-action) (even if I honestly don't know why you would)
298
- * `cocooned:after-reindex`: called after `position` fields update
329
+ ## Javascript
299
330
 
300
- To listen to the events in your JavaScript:
331
+ For more documentation about the JavaScript bundled in the companion package, please refer to [its own documentation](https://github.com/notus-sh/cocooned/blob/master/npm/README.md).
301
332
 
302
- ```javascript
303
- $('#container').on('cocooned:before-insert', function(event, node, cocoonedInstance) {
304
- /* Do something */
305
- });
333
+ ## Styling forms
334
+
335
+ Cocooned now uses exclusively data-attribute to hook JavaScript methods on but usual classes are still here and will stay so you can style your forms:
336
+
337
+ - `.cocooned-container` on a container
338
+ - `.cocooned-item` on an item
339
+ - `.cocooned-add` on an add trigger (link or button)
340
+ - `.cocooned-remove` on a remove trigger (link or button)
341
+ - `.cocooned-move-up` on a move up trigger (link or button)
342
+ - `.cocooned-move-down` on a move down trigger (link or button)
343
+
344
+ ## Migration from a previous version
345
+
346
+ These migrations steps only highlight major changes. When upgrading from a previous version, always refer to [the CHANGELOG](https://github.com/notus-sh/cocooned/blob/main/CHANGELOG.md) for new features and breaking changes.
347
+
348
+ ### From Cocooned ~1.0
349
+
350
+ #### Forms markup
351
+
352
+ Cocooned 2.0 introduced the `cocooned_container` and `cocooned_item` helpers to respectively wrap the container where items will be added and each of the items.
353
+
354
+ If you used Cocooned ~1.0, you should modify your main forms as follow:
355
+
356
+ ```diff
357
+ - <div data-cocooned-options="<%= {}.to_json %>">
358
+ + <%= cocooned_container do %>
359
+ <% # […] %>
360
+ + <% end %>
361
+ - </div>
306
362
  ```
307
363
 
308
- An event handler is called with 3 arguments:
364
+ And your nested partials:
365
+
366
+ ```diff
367
+ - <div class="cocooned-item">
368
+ + <%= cocooned_item do %>
369
+ <% # […] %>
370
+ + <% end %>
371
+ - </div>
372
+ ```
373
+
374
+ Support for the `data-cocooned-options` attribute to identify a container and the `.cocooned-item` class to identify an item is still here but it is not the recommended way to tag your containers and items anymore.
375
+
376
+ **Compatibility with older markup will be dropped in the next major release.**
377
+
378
+ #### Bundled styles
379
+
380
+ Cocooned ~2.0 does not provide any styles anymore. If you used to require (with Sprockets) or import the cocooned stylesheets into your application, you need to remove it.
381
+
382
+ **Empty files are included to not break your assets pipeline but will be removed in the next major release.**
309
383
 
310
- The event `event` is an instance of `jQuery.Event` and carry some additional data:
384
+ ### From Cocoon (any version)
311
385
 
312
- * `event.link`, the clicked link
313
- * `event.node`, the nested item that will be added, removed or moved, as a jQuery object. This is null for `cocooned:limit-reached` and `cocooned:*-reindex` events
314
- * `event.nodes`, the nested items that will be or just have been reindexed on `cocooned:*-reindex` events, as a jQuery object. Null otherwise.
315
- * `event.cocooned`, the Cocooned javascript object instance handling the nested association.
316
- * `event.originalEvent`, the original (browser) event.
386
+ Cocoon uses a `.nested_fields` class to identify items in a nested form and nothing to identify containers new items will be added to.
317
387
 
318
- The `node` argument is the same jQuery object as `event.node`.
319
- The `cocooned` argument is the same as `event.cocooned`.
388
+ If you used Cocoon, you should:
320
389
 
321
- #### Canceling an action
390
+ 1. Modify your forms and sub forms to use the `cocooned_container` and `cocooned_item` helpers. (See above for examples)
391
+ 2. Replace calls to `link_to_add_association` by `cocooned_add_item_link`
392
+ 3. Replace calls to `link_to_remove_association` by `cocooned_remove_item_link`
393
+ 4. Rename your I18n keys to use the `cocooned` namespace instead of `cocoon`
322
394
 
323
- You can cancel an action within the `cocooned:before-<action>` callback by calling `event.preventDefault()` or `event.stopPropagation()`.
395
+ **Compatibility with the original Cocoon API will be dropped in the next major release.**
@@ -1,15 +1,3 @@
1
- /* globals Cocooned */
2
1
  //= require 'cocooned'
3
2
 
4
- // Compatibility with the original Cocoon
5
- // TODO: Remove in 3.0
6
- function initCocoon () {
7
- $(Cocooned.prototype.selector('add')).each(function (_i, addLink) {
8
- var container = Cocooned.prototype.findContainer(addLink);
9
- var limit = parseInt($(addLink).data('limit'), 10) || false;
10
-
11
- container.cocooned({ limit: limit });
12
- });
13
- }
14
-
15
- $(initCocoon);
3
+ /* TODO: Remove in 3.0 */