cocooned 2.5.0 → 3.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/app/assets/javascripts/cocoon.js +1 -1
- data/app/assets/javascripts/cocooned.js +215 -137
- data/app/assets/stylesheets/cocoon.css +1 -1
- data/app/assets/stylesheets/cocooned.css +1 -1
- data/cocooned.gemspec +3 -3
- data/lib/cocooned/deprecation.rb +6 -6
- data/lib/cocooned/helpers/containers.rb +19 -8
- data/lib/cocooned/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0880ac255708fd7f92c868a50b53f4feb7d96d6554ada41f355b05d92d1c8bbe'
|
|
4
|
+
data.tar.gz: 682e140504a57d6065fc793c4f958e5fd4e581cb3bbf54f8d6573298b07f3f23
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b8601774bc1290dafe7df2cc7491f0dd88ebc770a6d05d68e978dff93b3570612414e722600d9ec029461c0c64696d497d08cc5736ce98cdea5e77ac0c320949
|
|
7
|
+
data.tar.gz: e50005f7b8b2c6baae3d39972f55e19b6fcc697bec297a02c22730e24588a48e3a9b422c7e42a09e9be4e2e932c699b985c81dac3ae62f99688b57f58945de09
|
data/CHANGELOG.md
CHANGED
|
@@ -6,8 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## Version 3.0.1 (2026-03-15)
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
* Ensure `required` attributes is disabled on all input types (#127, thanks @mattmenefee)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
* Compatibility with `formtastic ~> 6.0` (#130)
|
|
18
|
+
|
|
19
|
+
## Version 3.0.0 (2026-01-27)
|
|
20
|
+
|
|
21
|
+
### Breaking changes
|
|
22
|
+
|
|
23
|
+
* Respect browser accessibility settings on reduced motion to determinate the default value of the `animate` option (#120)
|
|
24
|
+
`animate` will be set to false by default if the `(prefers-reduced-motion: reduce)` media query matches.
|
|
25
|
+
* Proper instances cleanup through [explicit resource management](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Resource_management) (#115)
|
|
26
|
+
Introduce a `dispose` method on Cocooned instances to clean up what was done in the `start` method: remove event listeners and delete references to the disposed instance, so it can be cleanly garbage collected.
|
|
27
|
+
This may require you to install a polyfill for `DisposableStack` and `[Symbol.dispose]()` to support older browsers.
|
|
28
|
+
|
|
29
|
+
To not introduce too many breaking changes in a single release, removal of long-time deprecated features have been postponed to Cocooned 4.0 (#122)
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
* Optional Stimulus integration (#119)
|
|
34
|
+
Introduce two ways to integrate Cocooned with Stimulus: `registerCocoonedContainer` and `useCocooned`. See documentation for details.
|
|
35
|
+
|
|
9
36
|
### Changed
|
|
10
37
|
|
|
38
|
+
* Add Ruby 4.0 to the test matrix (#112)
|
|
39
|
+
|
|
11
40
|
## Version 2.5.0 (2025-10-23)
|
|
12
41
|
|
|
13
42
|
* Upgrade to use `eslint ~> 9` (#103)
|
|
@@ -36,6 +36,153 @@
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
class Traverser {
|
|
40
|
+
constructor (origin, traversal) {
|
|
41
|
+
this.#origin = origin;
|
|
42
|
+
this.#traversal = traversal;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
resolve (selector) {
|
|
46
|
+
if (this.#traversal in this.#origin && typeof this.#origin[this.#traversal] === 'function') {
|
|
47
|
+
return this._tryMethod(this.#traversal, selector)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (this.#traversal in this.#origin) {
|
|
51
|
+
return this._tryProperty(this.#traversal)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const method = `_${this.#traversal}`;
|
|
55
|
+
if (method in this) {
|
|
56
|
+
return this[method](selector)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Protected and private attributes and methods */
|
|
63
|
+
#origin
|
|
64
|
+
#traversal
|
|
65
|
+
|
|
66
|
+
_tryMethod (method, selector) {
|
|
67
|
+
try {
|
|
68
|
+
const resolved = this.#origin[method](selector);
|
|
69
|
+
if (resolved instanceof HTMLElement) {
|
|
70
|
+
return resolved
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {}
|
|
73
|
+
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_tryProperty (property) {
|
|
78
|
+
const resolved = this.#origin[property];
|
|
79
|
+
if (resolved instanceof HTMLElement) {
|
|
80
|
+
return resolved
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_parent (selector) {
|
|
87
|
+
if (this.#origin.parentElement.matches(selector)) {
|
|
88
|
+
return this.#origin.parentElement
|
|
89
|
+
}
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
_prev (selector) {
|
|
94
|
+
if (this.#origin.previousElementSibling.matches(selector)) {
|
|
95
|
+
return this.#origin.previousElementSibling
|
|
96
|
+
}
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_next (selector) {
|
|
101
|
+
if (this.#origin.nextElementSibling.matches(selector)) {
|
|
102
|
+
return this.#origin.nextElementSibling
|
|
103
|
+
}
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_siblings (selector) {
|
|
108
|
+
return this.#origin.parentElement.querySelector(selector)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class Deprecator {
|
|
113
|
+
logger
|
|
114
|
+
package
|
|
115
|
+
version
|
|
116
|
+
|
|
117
|
+
constructor (version, packageName, logger) {
|
|
118
|
+
this.version = version;
|
|
119
|
+
this.package = packageName;
|
|
120
|
+
this.logger = logger;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
warn (message, replacement = null) {
|
|
124
|
+
if (message in this.#emitted) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const warning = `${message}. It will be removed from ${this.package} ${this.version}`;
|
|
129
|
+
const alternative = (replacement !== null ? `, use ${replacement} instead` : '');
|
|
130
|
+
this.logger.warn(`DEPRECATION WARNING: ${warning}${alternative}.`);
|
|
131
|
+
|
|
132
|
+
this.#emitted[message] = true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Protected and private attributes and methods */
|
|
136
|
+
#emitted = Object.create(null)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const deprecators = Object.create(null);
|
|
140
|
+
|
|
141
|
+
function deprecator (version, packageName = 'Cocooned', logger = console) {
|
|
142
|
+
const hash = [version, packageName].join('#');
|
|
143
|
+
if (!(hash in deprecators)) {
|
|
144
|
+
deprecators[hash] = new Deprecator(version, packageName, logger);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return deprecators[hash]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
class Listener {
|
|
151
|
+
constructor (eventTarget, type, listener) {
|
|
152
|
+
this.#eventTarget = eventTarget;
|
|
153
|
+
this.#type = type;
|
|
154
|
+
this.#listener = listener;
|
|
155
|
+
|
|
156
|
+
this.#eventTarget.addEventListener(this.#type, this.#listener);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
dispose () {
|
|
160
|
+
this.#eventTarget.removeEventListener(this.#type, this.#listener);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Protected and private attributes and methods */
|
|
164
|
+
#eventTarget
|
|
165
|
+
#type
|
|
166
|
+
#listener
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
disposable(Listener);
|
|
170
|
+
|
|
171
|
+
if (typeof Symbol.dispose !== 'symbol') {
|
|
172
|
+
console.warn(`
|
|
173
|
+
Cocooned use Disposable objects but they are not supported by your browser.
|
|
174
|
+
See Cocooned documentation for polyfill options.
|
|
175
|
+
`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function disposable (klass) {
|
|
179
|
+
if (typeof Symbol.dispose !== 'symbol') {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
klass.prototype[Symbol.dispose] = klass.prototype.dispose;
|
|
184
|
+
}
|
|
185
|
+
|
|
39
186
|
// Borrowed from <https://stackoverflow.com/a/2117523>
|
|
40
187
|
function uuidv4 () {
|
|
41
188
|
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
|
|
@@ -69,13 +216,22 @@
|
|
|
69
216
|
]
|
|
70
217
|
}
|
|
71
218
|
|
|
219
|
+
const canAnimate = (
|
|
220
|
+
'animate' in document.createElement('div') &&
|
|
221
|
+
typeof document.createElement('div').animate === 'function'
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const shouldAnimate = (
|
|
225
|
+
'matchMedia' in window &&
|
|
226
|
+
!window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
227
|
+
);
|
|
228
|
+
|
|
72
229
|
const instances = Object.create(null);
|
|
73
230
|
|
|
74
231
|
class Base {
|
|
75
232
|
static get defaultOptions () {
|
|
76
|
-
const element = document.createElement('div');
|
|
77
233
|
return {
|
|
78
|
-
animate:
|
|
234
|
+
animate: canAnimate && shouldAnimate,
|
|
79
235
|
animator: defaultAnimator,
|
|
80
236
|
duration: 450
|
|
81
237
|
}
|
|
@@ -98,7 +254,7 @@
|
|
|
98
254
|
|
|
99
255
|
constructor (container, options) {
|
|
100
256
|
this._container = container;
|
|
101
|
-
this.
|
|
257
|
+
this.__uuid = uuidv4();
|
|
102
258
|
this._options = this.constructor._normalizeOptions({
|
|
103
259
|
...this.constructor.defaultOptions,
|
|
104
260
|
...('cocoonedOptions' in container.dataset ? JSON.parse(container.dataset.cocoonedOptions) : {}),
|
|
@@ -115,16 +271,31 @@
|
|
|
115
271
|
}
|
|
116
272
|
|
|
117
273
|
start () {
|
|
118
|
-
this.container.dataset
|
|
119
|
-
|
|
120
|
-
|
|
274
|
+
if (!('cocoonedContainer' in this.container.dataset)) {
|
|
275
|
+
deprecator('4.0').warn(
|
|
276
|
+
'CSS classes based detection is deprecated',
|
|
277
|
+
'cocooned_container Rails helper to declare containers'
|
|
278
|
+
);
|
|
279
|
+
this.container.dataset.cocoonedContainer = true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.container.dataset.cocoonedUuid = this.__uuid;
|
|
283
|
+
this._onDispose(() => delete this.container.dataset.cocoonedUuid);
|
|
284
|
+
|
|
285
|
+
instances[this.__uuid] = this;
|
|
286
|
+
this._onDispose(() => delete instances[this.__uuid]);
|
|
121
287
|
|
|
122
288
|
const hideDestroyed = () => { hideMarkedForDestruction(this, this.items); };
|
|
123
289
|
|
|
124
290
|
hideDestroyed();
|
|
125
|
-
this.container.ownerDocument
|
|
126
|
-
this.container.ownerDocument
|
|
127
|
-
this.container.ownerDocument
|
|
291
|
+
this._addEventListener(this.container.ownerDocument, 'page:load', hideDestroyed);
|
|
292
|
+
this._addEventListener(this.container.ownerDocument, 'turbo:load', hideDestroyed);
|
|
293
|
+
this._addEventListener(this.container.ownerDocument, 'turbolinks:load', hideDestroyed);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
dispose () {
|
|
297
|
+
this._disposer.dispose();
|
|
298
|
+
this._container = null;
|
|
128
299
|
}
|
|
129
300
|
|
|
130
301
|
notify (node, eventType, eventData) {
|
|
@@ -180,8 +351,21 @@
|
|
|
180
351
|
|
|
181
352
|
_container
|
|
182
353
|
_options
|
|
183
|
-
|
|
354
|
+
__disposer
|
|
184
355
|
__emitter
|
|
356
|
+
__uuid
|
|
357
|
+
|
|
358
|
+
_addEventListener (target, type, listener) {
|
|
359
|
+
this._disposer.use(new Listener(target, type, listener));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
get _disposer () {
|
|
363
|
+
if (typeof this.__disposer === 'undefined') {
|
|
364
|
+
this.__disposer = new DisposableStack();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return this.__disposer
|
|
368
|
+
}
|
|
185
369
|
|
|
186
370
|
get _emitter () {
|
|
187
371
|
if (typeof this.__emitter === 'undefined') {
|
|
@@ -191,6 +375,10 @@
|
|
|
191
375
|
return this.__emitter
|
|
192
376
|
}
|
|
193
377
|
|
|
378
|
+
_onDispose (callback) {
|
|
379
|
+
this._disposer.defer(callback);
|
|
380
|
+
}
|
|
381
|
+
|
|
194
382
|
_selectors (name) {
|
|
195
383
|
return this.constructor.selectors[name]
|
|
196
384
|
}
|
|
@@ -205,6 +393,8 @@
|
|
|
205
393
|
}
|
|
206
394
|
}
|
|
207
395
|
|
|
396
|
+
disposable(Base);
|
|
397
|
+
|
|
208
398
|
class Trigger {
|
|
209
399
|
constructor (trigger, cocooned) {
|
|
210
400
|
this._trigger = trigger;
|
|
@@ -277,117 +467,6 @@
|
|
|
277
467
|
}
|
|
278
468
|
}
|
|
279
469
|
|
|
280
|
-
class Traverser {
|
|
281
|
-
constructor (origin, traversal) {
|
|
282
|
-
this.#origin = origin;
|
|
283
|
-
this.#traversal = traversal;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
resolve (selector) {
|
|
287
|
-
if (this.#traversal in this.#origin && typeof this.#origin[this.#traversal] === 'function') {
|
|
288
|
-
return this._tryMethod(this.#traversal, selector)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (this.#traversal in this.#origin) {
|
|
292
|
-
return this._tryProperty(this.#traversal)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const method = `_${this.#traversal}`;
|
|
296
|
-
if (method in this) {
|
|
297
|
-
return this[method](selector)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return null
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/* Protected and private attributes and methods */
|
|
304
|
-
#origin
|
|
305
|
-
#traversal
|
|
306
|
-
|
|
307
|
-
_tryMethod (method, selector) {
|
|
308
|
-
try {
|
|
309
|
-
const resolved = this.#origin[method](selector);
|
|
310
|
-
if (resolved instanceof HTMLElement) {
|
|
311
|
-
return resolved
|
|
312
|
-
}
|
|
313
|
-
} catch (e) {}
|
|
314
|
-
|
|
315
|
-
return null
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
_tryProperty (property) {
|
|
319
|
-
const resolved = this.#origin[property];
|
|
320
|
-
if (resolved instanceof HTMLElement) {
|
|
321
|
-
return resolved
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return null
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
_parent (selector) {
|
|
328
|
-
if (this.#origin.parentElement.matches(selector)) {
|
|
329
|
-
return this.#origin.parentElement
|
|
330
|
-
}
|
|
331
|
-
return null
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
_prev (selector) {
|
|
335
|
-
if (this.#origin.previousElementSibling.matches(selector)) {
|
|
336
|
-
return this.#origin.previousElementSibling
|
|
337
|
-
}
|
|
338
|
-
return null
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
_next (selector) {
|
|
342
|
-
if (this.#origin.nextElementSibling.matches(selector)) {
|
|
343
|
-
return this.#origin.nextElementSibling
|
|
344
|
-
}
|
|
345
|
-
return null
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
_siblings (selector) {
|
|
349
|
-
return this.#origin.parentElement.querySelector(selector)
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
class Deprecator {
|
|
354
|
-
logger
|
|
355
|
-
package
|
|
356
|
-
version
|
|
357
|
-
|
|
358
|
-
constructor (version, packageName, logger) {
|
|
359
|
-
this.version = version;
|
|
360
|
-
this.package = packageName;
|
|
361
|
-
this.logger = logger;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
warn (message, replacement = null) {
|
|
365
|
-
if (message in this.#emitted) {
|
|
366
|
-
return
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const warning = `${message}. It will be removed from ${this.package} ${this.version}`;
|
|
370
|
-
const alternative = (replacement !== null ? `, use ${replacement} instead` : '');
|
|
371
|
-
this.logger.warn(`DEPRECATION WARNING: ${warning}${alternative}.`);
|
|
372
|
-
|
|
373
|
-
this.#emitted[message] = true;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/* Protected and private attributes and methods */
|
|
377
|
-
#emitted = Object.create(null)
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const deprecators = Object.create(null);
|
|
381
|
-
|
|
382
|
-
function deprecator (version, packageName = 'Cocooned', logger = console) {
|
|
383
|
-
const hash = [version, packageName].join('#');
|
|
384
|
-
if (!(hash in deprecators)) {
|
|
385
|
-
deprecators[hash] = new Deprecator(version, packageName, logger);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return deprecators[hash]
|
|
389
|
-
}
|
|
390
|
-
|
|
391
470
|
class Extractor {
|
|
392
471
|
constructor (trigger, cocooned) {
|
|
393
472
|
this.#trigger = trigger;
|
|
@@ -466,7 +545,7 @@
|
|
|
466
545
|
return this.#trigger.ownerDocument.querySelector(node)
|
|
467
546
|
}
|
|
468
547
|
|
|
469
|
-
deprecator('
|
|
548
|
+
deprecator('4.0').warn('associationInsertionTraversal is deprecated');
|
|
470
549
|
const traverser = new Traverser(this.#trigger, this.#dataset.associationInsertionTraversal);
|
|
471
550
|
|
|
472
551
|
return traverser.resolve(node)
|
|
@@ -621,8 +700,7 @@
|
|
|
621
700
|
|
|
622
701
|
_markForDestruction () {
|
|
623
702
|
this._item.querySelector('input[type=hidden][name$="[_destroy]"]').setAttribute('value', 'true');
|
|
624
|
-
this._item.querySelectorAll('
|
|
625
|
-
.forEach(input => input.removeAttribute('required'));
|
|
703
|
+
this._item.querySelectorAll('[required]').forEach(input => input.removeAttribute('required'));
|
|
626
704
|
}
|
|
627
705
|
}
|
|
628
706
|
|
|
@@ -736,12 +814,12 @@
|
|
|
736
814
|
.map(element => Add.create(element, this))
|
|
737
815
|
.filter(trigger => this.toContainer(trigger.insertionNode) === this.container);
|
|
738
816
|
|
|
739
|
-
this.addTriggers.forEach(add =>
|
|
740
|
-
'click',
|
|
741
|
-
|
|
742
|
-
));
|
|
817
|
+
this.addTriggers.forEach(add => {
|
|
818
|
+
this._addEventListener(add.trigger, 'click', clickHandler$1((e) => add.handle(e)));
|
|
819
|
+
});
|
|
743
820
|
|
|
744
|
-
this.
|
|
821
|
+
this._addEventListener(
|
|
822
|
+
this.container,
|
|
745
823
|
'click',
|
|
746
824
|
itemDelegatedClickHandler(this, this._selector('triggers.remove'), (e) => {
|
|
747
825
|
const trigger = new Remove(e.target, this);
|
|
@@ -795,7 +873,7 @@
|
|
|
795
873
|
return
|
|
796
874
|
}
|
|
797
875
|
|
|
798
|
-
this.container
|
|
876
|
+
this._addEventListener(this.container, 'cocooned:before-insert', e => {
|
|
799
877
|
if (this.items.length < this.options.limit) {
|
|
800
878
|
return
|
|
801
879
|
}
|
|
@@ -943,16 +1021,16 @@
|
|
|
943
1021
|
return
|
|
944
1022
|
}
|
|
945
1023
|
|
|
946
|
-
this.container
|
|
947
|
-
this.container
|
|
948
|
-
this.container
|
|
1024
|
+
this._addEventListener(this.container, 'cocooned:after-insert', e => this._reindexer.reindex(e));
|
|
1025
|
+
this._addEventListener(this.container, 'cocooned:after-remove', e => this._reindexer.reindex(e));
|
|
1026
|
+
this._addEventListener(this.container, 'cocooned:after-move', e => this._reindexer.reindex(e));
|
|
949
1027
|
const form = this.container.closest('form');
|
|
950
1028
|
if (form !== null) {
|
|
951
|
-
|
|
1029
|
+
this._addEventListener(form, 'submit', e => this._reindexer.reindex(e));
|
|
952
1030
|
}
|
|
953
1031
|
|
|
954
|
-
this.container
|
|
955
|
-
this.container
|
|
1032
|
+
this._addEventListener(this.container, 'click', clickHandler(this, this._selector('triggers.up'), Up));
|
|
1033
|
+
this._addEventListener(this.container, 'click', clickHandler(this, this._selector('triggers.down'), Down));
|
|
956
1034
|
}
|
|
957
1035
|
|
|
958
1036
|
/* Protected and private attributes and methods */
|
|
@@ -1051,7 +1129,7 @@
|
|
|
1051
1129
|
|
|
1052
1130
|
$(() => cocoonAutoStart($));
|
|
1053
1131
|
|
|
1054
|
-
deprecator('
|
|
1132
|
+
deprecator('4.0').warn(
|
|
1055
1133
|
'Loading @notus.sh/cocooned/cocooned is deprecated',
|
|
1056
1134
|
'@notus.sh/cocooned/jquery, @notus.sh/cocooned or `@notus.sh/cocooned/src/cocooned/cocooned`'
|
|
1057
1135
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
/* TODO: Remove in
|
|
1
|
+
/* TODO: Remove in 4.0 */
|
data/cocooned.gemspec
CHANGED
|
@@ -13,8 +13,8 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
|
|
14
14
|
spec.summary = 'Form builder agnostic handling of Rails nested forms'
|
|
15
15
|
spec.description = <<-DESC.gsub(/\s+/, ' ')
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Rails nested form made easy, with standard Rails form builder, Formtastic or SimpleForm.
|
|
17
|
+
Unobtrusive and zero-dependencies JavaScript included, with optional integration with jQuery and Stimulus.
|
|
18
18
|
DESC
|
|
19
19
|
spec.homepage = 'https://github.com/notus-sh/cocooned'
|
|
20
20
|
|
|
@@ -42,7 +42,7 @@ Gem::Specification.new do |spec|
|
|
|
42
42
|
|
|
43
43
|
spec.add_dependency 'rails', '>= 7.2', '< 8.2'
|
|
44
44
|
|
|
45
|
-
spec.add_development_dependency 'bundler', '
|
|
45
|
+
spec.add_development_dependency 'bundler', '>= 2.1'
|
|
46
46
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
47
47
|
spec.add_development_dependency 'rspec', '~> 3.11'
|
|
48
48
|
end
|
data/lib/cocooned/deprecation.rb
CHANGED
|
@@ -26,14 +26,14 @@ module Cocooned
|
|
|
26
26
|
cocooned_add_item_link(...)
|
|
27
27
|
end
|
|
28
28
|
deprecate link_to_add_association: 'Use :cocooned_add_link instead',
|
|
29
|
-
deprecator: Deprecation['
|
|
29
|
+
deprecator: Deprecation['4.0']
|
|
30
30
|
|
|
31
31
|
# @deprecated: Please use {#cocooned_remove_item_link} instead
|
|
32
32
|
def link_to_remove_association(...)
|
|
33
33
|
cocooned_remove_item_link(...)
|
|
34
34
|
end
|
|
35
35
|
deprecate link_to_remove_association: 'Use :cocooned_remove_item_link instead',
|
|
36
|
-
deprecator: Deprecation['
|
|
36
|
+
deprecator: Deprecation['4.0']
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -44,7 +44,7 @@ module Cocooned
|
|
|
44
44
|
def i18n_namespaces
|
|
45
45
|
return super unless I18n.exists?(:cocoon)
|
|
46
46
|
|
|
47
|
-
Deprecation['
|
|
47
|
+
Deprecation['4.0'].warn 'Support for the :cocoon i18n namespace will be removed in 4.0', caller_locations(3)
|
|
48
48
|
super + %w[cocoon]
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -57,7 +57,7 @@ module Cocooned
|
|
|
57
57
|
def html_data
|
|
58
58
|
return super unless data_keys.size.positive?
|
|
59
59
|
|
|
60
|
-
Deprecation['
|
|
60
|
+
Deprecation['4.0'].warn 'Compatibility with options named data-* will be removed in 4.0', caller_locations(3)
|
|
61
61
|
html_data_normalize super.merge(data_options)
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -78,7 +78,7 @@ module Cocooned
|
|
|
78
78
|
|
|
79
79
|
def association_options
|
|
80
80
|
if options.key? :insertion_traversal
|
|
81
|
-
Deprecation['
|
|
81
|
+
Deprecation['4.0'].warn 'Support for the :insertion_traversal will be removed in 4.0', caller_locations(3)
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
super
|
|
@@ -91,7 +91,7 @@ module Cocooned
|
|
|
91
91
|
def renderer_options
|
|
92
92
|
return super unless options.key?(:render_options)
|
|
93
93
|
|
|
94
|
-
Deprecation['
|
|
94
|
+
Deprecation['4.0'].warn 'Support for :render_options will be removed in 4.0', caller_locations(3)
|
|
95
95
|
legacy_options = options.delete(:render_options)
|
|
96
96
|
|
|
97
97
|
super.tap do |opts|
|
|
@@ -32,10 +32,8 @@ module Cocooned
|
|
|
32
32
|
def cocooned_container(*args, &)
|
|
33
33
|
options = args.extract_options!.dup
|
|
34
34
|
name = args.shift || :div
|
|
35
|
-
defaults = cocooned_wrapper_defaults(options, %w[cocooned-container], :'cocooned-container')
|
|
36
|
-
defaults[:data][:cocooned_options] = options.extract!(:limit, :reorderable).to_json
|
|
37
35
|
|
|
38
|
-
content_tag(name, *args, **options
|
|
36
|
+
content_tag(name, *args, **cocooned_container_options(options), &)
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
# Wrap content with the expected markup for a Cocooned item.
|
|
@@ -56,17 +54,30 @@ module Cocooned
|
|
|
56
54
|
def cocooned_item(*args, &)
|
|
57
55
|
options = args.extract_options!.dup
|
|
58
56
|
name = args.shift || :div
|
|
59
|
-
defaults = cocooned_wrapper_defaults(options, %w[cocooned-item nested-fields], :'cocooned-item')
|
|
60
57
|
|
|
61
|
-
content_tag(name, *args, **options
|
|
58
|
+
content_tag(name, *args, **cocooned_item_options(options), &)
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
protected
|
|
65
62
|
|
|
66
|
-
def
|
|
67
|
-
|
|
63
|
+
def cocooned_container_options(options)
|
|
64
|
+
options.deep_merge(
|
|
65
|
+
class: token_list(options.delete(:class), %w[cocooned-container]),
|
|
66
|
+
data: {
|
|
67
|
+
controller: [options.dig(:data, :controller), :cocooned].compact_blank.join(' '),
|
|
68
|
+
cocooned_container: true,
|
|
69
|
+
cocooned_options: options.extract!(:limit, :reorderable).to_json
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
end
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
def cocooned_item_options(options)
|
|
75
|
+
options.deep_merge(
|
|
76
|
+
class: token_list(options.delete(:class), %w[cocooned-item nested-fields]),
|
|
77
|
+
data: {
|
|
78
|
+
cocooned_item: true
|
|
79
|
+
}
|
|
80
|
+
)
|
|
70
81
|
end
|
|
71
82
|
end
|
|
72
83
|
end
|
data/lib/cocooned/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cocooned
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gaël-Ian Havard
|
|
@@ -34,14 +34,14 @@ dependencies:
|
|
|
34
34
|
name: bundler
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '2.1'
|
|
40
40
|
type: :development
|
|
41
41
|
prerelease: false
|
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '2.1'
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
@@ -72,9 +72,9 @@ dependencies:
|
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: '3.11'
|
|
75
|
-
description: "
|
|
76
|
-
or
|
|
77
|
-
|
|
75
|
+
description: " Rails nested form made easy, with standard Rails form builder, Formtastic
|
|
76
|
+
or SimpleForm. Unobtrusive and zero-dependencies JavaScript included, with optional
|
|
77
|
+
integration with jQuery and Stimulus. "
|
|
78
78
|
email:
|
|
79
79
|
- gael-ian@notus.sh
|
|
80
80
|
- nathan@dixis.com
|
|
@@ -136,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
137
|
version: '0'
|
|
138
138
|
requirements: []
|
|
139
|
-
rubygems_version:
|
|
139
|
+
rubygems_version: 4.0.3
|
|
140
140
|
specification_version: 4
|
|
141
141
|
summary: Form builder agnostic handling of Rails nested forms
|
|
142
142
|
test_files: []
|