cocooned 1.4.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +178 -0
- data/README.md +244 -172
- data/app/assets/javascripts/cocoon.js +1 -13
- data/app/assets/javascripts/cocooned.js +929 -399
- data/app/assets/stylesheets/cocooned.css +1 -9
- data/cocooned.gemspec +27 -18
- data/lib/cocooned/association/builder.rb +66 -0
- data/lib/cocooned/association/renderer.rb +53 -0
- data/lib/cocooned/association.rb +8 -0
- data/lib/cocooned/deprecation.rb +105 -0
- data/lib/cocooned/helpers/containers.rb +72 -0
- data/lib/cocooned/helpers/tags/add.rb +136 -0
- data/lib/cocooned/helpers/tags/down.rb +76 -0
- data/lib/cocooned/helpers/tags/remove.rb +78 -0
- data/lib/cocooned/helpers/tags/up.rb +76 -0
- data/lib/cocooned/helpers/tags.rb +60 -0
- data/lib/cocooned/helpers.rb +3 -329
- data/lib/cocooned/railtie.rb +7 -2
- data/lib/cocooned/tags/add.rb +61 -0
- data/lib/cocooned/tags/base.rb +61 -0
- data/lib/cocooned/tags/down.rb +19 -0
- data/lib/cocooned/tags/remove.rb +35 -0
- data/lib/cocooned/tags/up.rb +19 -0
- data/lib/cocooned/tags.rb +12 -0
- data/lib/cocooned/tags_helper.rb +83 -0
- data/lib/cocooned/version.rb +1 -1
- data/lib/cocooned.rb +6 -1
- metadata +51 -86
- data/History.md +0 -283
- data/Rakefile +0 -113
- data/lib/cocooned/association_builder.rb +0 -68
- data/lib/cocooned/helpers/cocoon_compatibility.rb +0 -27
- data/lib/cocooned/helpers/deprecate.rb +0 -47
data/README.md
CHANGED
@@ -1,77 +1,105 @@
|
|
1
1
|
# Cocooned
|
2
2
|
|
3
|
-
[![
|
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
|
6
|
+
Cocooned makes it easier to handle nested forms in Rails.
|
6
7
|
|
7
|
-
Cocooned is form builder-agnostic: it works with standard Rails (>=
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
32
|
+
Add `cocooned` to your `Gemfile`:
|
26
33
|
|
27
34
|
```ruby
|
28
|
-
gem
|
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
|
-
|
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
|
-
|
66
|
+
```javascript
|
67
|
+
//= require 'cocooned'
|
68
|
+
```
|
34
69
|
|
35
|
-
|
70
|
+
**This compatibility with aging Rails assets pipelines will be removed in the next major release.**
|
36
71
|
|
37
|
-
##
|
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
|
-
|
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
|
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 |
|
83
|
-
<%=
|
110
|
+
<%= form_for @list do |form| %>
|
111
|
+
<%= form.text_field :name %>
|
84
112
|
|
85
113
|
<h3>Items</h3>
|
86
|
-
<%=
|
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
|
-
<%=
|
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.
|
101
|
-
3. Add a way to
|
102
|
-
4.
|
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
|
-
|
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
|
-
|
135
|
+
### 1. Move the nested form to a partial
|
109
136
|
|
110
|
-
|
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.
|
142
|
+
<%= form.text_field :name %>
|
114
143
|
|
115
144
|
<h3>Items</h3>
|
116
145
|
<%= form.fields_for :items do |item_form|
|
117
|
-
|
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
|
-
|
165
|
+
### 2. Signal to Cocooned it should handle your form
|
132
166
|
|
133
|
-
|
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
|
-
|
140
|
-
<%= form.fields_for :
|
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
|
-
|
185
|
+
And your sub form as follow:
|
154
186
|
|
155
|
-
|
156
|
-
|
157
|
-
```erb
|
187
|
+
```diff
|
158
188
|
<% # `app/views/lists/_item_fields.html.erb` %>
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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 `
|
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
|
-
|
198
|
+
### 3. Add a way to add a new item to the list
|
170
199
|
|
171
|
-
|
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
|
-
```
|
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
|
-
|
181
|
-
<%= form.fields_for :
|
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
|
-
|
186
|
-
|
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
|
-
|
192
|
-
```
|
217
|
+
<% end %>
|
218
|
+
```
|
193
219
|
|
194
|
-
You'
|
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
|
-
###
|
222
|
+
### 4. Add a way to remove an item from the collection
|
197
223
|
|
198
|
-
|
224
|
+
Change your sub form as follow:
|
199
225
|
|
200
|
-
|
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
|
-
|
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
|
-
|
238
|
+
### Gotchas
|
206
239
|
|
207
|
-
|
240
|
+
#### Strong Parameters Gotcha
|
208
241
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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
|
-
####
|
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
|
-
|
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
|
-
|
235
|
-
|
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
|
-
|
277
|
+
### The reorderable plugin
|
254
278
|
|
255
|
-
|
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
|
-
|
292
|
+
<%= cocooned_item do %>
|
258
293
|
<%= f.label :description %>
|
259
294
|
<%= f.text_field :description %>
|
260
295
|
<%= f.check_box :done %>
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
-
|
300
|
+
<% end %>
|
266
301
|
```
|
267
302
|
|
268
|
-
|
303
|
+
Remember to add `:position` as a permitted parameter in your controller.
|
269
304
|
|
270
|
-
|
305
|
+
## Links or buttons?
|
271
306
|
|
272
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
316
|
+
## Internationalisation
|
281
317
|
|
282
|
-
|
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
|
-
|
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
|
-
|
322
|
+
You can declare default labels in your translation files with following keys:
|
290
323
|
|
291
|
-
|
324
|
+
- `cocooned.{association}.{action}` (Ex: `cocooned.items.add`)
|
325
|
+
- `cocooned.defaults.{action}`
|
292
326
|
|
293
|
-
|
327
|
+
If no translation is found, the default label will be the humanized action name.
|
294
328
|
|
295
|
-
|
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
|
-
|
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
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
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
|
-
|
384
|
+
### From Cocoon (any version)
|
311
385
|
|
312
|
-
|
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
|
-
|
319
|
-
The `cocooned` argument is the same as `event.cocooned`.
|
388
|
+
If you used Cocoon, you should:
|
320
389
|
|
321
|
-
|
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
|
-
|
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
|
-
|
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 */
|