katalyst-tables 2.2.12 → 2.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea4e17cca2f486696e4cc5b8bff2bc6b06aeb33e131f0774f881b324964eae90
4
- data.tar.gz: 5bc402aef25bee8f49418225d603f36246565622749f1fa6d06d922f341f6348
3
+ metadata.gz: 16f4a66ac1715abeba201402eedf3c19f27dbd0c53291b841164ad79f085ec77
4
+ data.tar.gz: 80aad00ee67b5d3d760a7b47ba3ffc735d704d49edb4e42942197c4d93ff8eea
5
5
  SHA512:
6
- metadata.gz: d87f264fb30eab8e5a9f43b1a85464f5b0fc32f499e11bc64acc745a804dda081ed58e3eb368b1a50b4e01eeb28b6ace4e9b550a86fb0addb2cf4fb0a61aa38d
7
- data.tar.gz: 3afaa276a44921bdb7a5b62d60701d77c71da24741a651b0fe3b89c36a2fc059b037fe1c5859680f6be43c0bb324fe4e9924067b2d0c2b58cffcad1c1b7b585a
6
+ metadata.gz: 6713a3de4a8ee62071f3c34e1557691add7b02428eee13f994b107261571de84b6be57b9f1c7c997ab6bd87a23a4fecf68dfd52b311a412e26399b099fac4c0e
7
+ data.tar.gz: 7fdd72501c7f780181fc2d3a0d182fd989c57a4aa4a4418a90a599c64fbc2bf1c960b27ebb8ce90ae9fdbdb9184c3020976d0f2b7cffbf9f19b5e704341e936c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.3.0]
4
+
5
+ - Remove support for Ruby < 3.0 (no longer used at Katalyst)
6
+
3
7
  ## [2.2.0]
4
8
 
5
9
  - Add support for ordinal columns with batch updating
data/README.md CHANGED
@@ -14,6 +14,16 @@ And then execute:
14
14
 
15
15
  $ bundle install
16
16
 
17
+ Add the Gem's javascript and CSS to your build pipeline. This assumes that
18
+ you're using `rails-dartsass` and `importmaps` to manage your assets.
19
+
20
+ ```javascript
21
+ // app/javascript/controllers/application.js
22
+ import { application } from "controllers/application";
23
+ import tables from "@katalyst/tables";
24
+ application.load(tables);
25
+ ```
26
+
17
27
  ## Usage
18
28
 
19
29
  This gem provides entry points for backend and frontend concerns:
@@ -0,0 +1,496 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ import { Turbo } from '@hotwired/turbo-rails';
3
+
4
+ class TurboCollectionController extends Controller {
5
+ static values = {
6
+ query: String,
7
+ sort: String,
8
+ };
9
+
10
+ queryValueChanged(query) {
11
+ Turbo.navigator.history.replace(this.#url(query));
12
+ }
13
+
14
+ sortValueChanged(sort) {
15
+ document.querySelectorAll(this.#sortSelector).forEach((input) => {
16
+ if (input) input.value = sort;
17
+ });
18
+ }
19
+
20
+ get #sortSelector() {
21
+ return "input[name='sort']";
22
+ }
23
+
24
+ #url(query) {
25
+ const frame = this.element.closest("turbo-frame");
26
+ let url;
27
+
28
+ if (frame) {
29
+ url = new URL(frame.baseURI);
30
+ } else {
31
+ url = new URL(window.location.href);
32
+ }
33
+
34
+ url.search = query;
35
+
36
+ return url;
37
+ }
38
+ }
39
+
40
+ class OrderableRowController extends Controller {
41
+ static values = {
42
+ params: Object,
43
+ };
44
+
45
+ connect() {
46
+ // index from server may be inconsistent with the visual ordering,
47
+ // especially if this is a new node. Use positional indexes instead,
48
+ // as these are the values we will send on save.
49
+ this.index = domIndex(this.row);
50
+ }
51
+
52
+ paramsValueChanged(params) {
53
+ this.id = params.id_value;
54
+ }
55
+
56
+ dragUpdate(offset) {
57
+ this.dragOffset = offset;
58
+ this.row.style.position = "relative";
59
+ this.row.style.top = offset + "px";
60
+ this.row.style.zIndex = "1";
61
+ this.row.toggleAttribute("dragging", true);
62
+ }
63
+
64
+ /**
65
+ * Called on items that are not the dragged item during drag. Updates the
66
+ * visual position of the item relative to the dragged item.
67
+ *
68
+ * @param index {number} intended index of the item during drag
69
+ */
70
+ updateVisually(index) {
71
+ this.row.style.position = "relative";
72
+ this.row.style.top = `${
73
+ this.row.offsetHeight * (index - this.dragIndex)
74
+ }px`;
75
+ }
76
+
77
+ /**
78
+ * Set the index value of the item. This is called on all items after a drop
79
+ * event. If the index is different to the params index then this item has
80
+ * changed.
81
+ *
82
+ * @param index {number} the new index value
83
+ */
84
+ updateIndex(index) {
85
+ this.index = index;
86
+ }
87
+
88
+ /**
89
+ * Restore any visual changes made during drag and remove the drag state.
90
+ */
91
+ reset() {
92
+ delete this.dragOffset;
93
+ this.row.removeAttribute("style");
94
+ this.row.removeAttribute("dragging");
95
+ }
96
+
97
+ /**
98
+ * @returns {boolean} true when the item has a change to its index value
99
+ */
100
+ get hasChanges() {
101
+ return this.paramsValue.index_value !== this.index;
102
+ }
103
+
104
+ /**
105
+ * Calculate the relative index of the item during drag. This is used to
106
+ * sort items during drag as it takes into account any uncommitted changes
107
+ * to index caused by the drag offset.
108
+ *
109
+ * @returns {number} index for the purposes of drag and drop ordering
110
+ */
111
+ get dragIndex() {
112
+ if (this.dragOffset && this.dragOffset !== 0) {
113
+ return this.index + Math.round(this.dragOffset / this.row.offsetHeight);
114
+ } else {
115
+ return this.index;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Index value for use in comparisons during drag. This is used to determine
121
+ * whether the dragged item is above or below another item. If this item is
122
+ * being dragged then we offset the index by 0.5 to ensure that it jumps up
123
+ * or down when it reaches the midpoint of the item above or below it.
124
+ *
125
+ * @returns {number}
126
+ */
127
+ get comparisonIndex() {
128
+ if (this.dragOffset) {
129
+ return this.dragIndex + (this.dragOffset > 0 ? 0.5 : -0.5);
130
+ } else {
131
+ return this.index;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * The containing row element.
137
+ *
138
+ * @returns {HTMLElement}
139
+ */
140
+ get row() {
141
+ return this.element.parentElement;
142
+ }
143
+ }
144
+
145
+ function domIndex(element) {
146
+ return Array.from(element.parentElement.children).indexOf(element);
147
+ }
148
+
149
+ class OrderableListController extends Controller {
150
+ static outlets = ["tables--orderable--item", "tables--orderable--form"];
151
+
152
+ //region State transitions
153
+
154
+ startDragging(dragState) {
155
+ this.dragState = dragState;
156
+
157
+ document.addEventListener("mousemove", this.mousemove);
158
+ document.addEventListener("mouseup", this.mouseup);
159
+ window.addEventListener("scroll", this.scroll, true);
160
+
161
+ this.element.style.position = "relative";
162
+ }
163
+
164
+ stopDragging() {
165
+ const dragState = this.dragState;
166
+ delete this.dragState;
167
+
168
+ document.removeEventListener("mousemove", this.mousemove);
169
+ document.removeEventListener("mouseup", this.mouseup);
170
+ window.removeEventListener("scroll", this.scroll, true);
171
+
172
+ this.element.removeAttribute("style");
173
+ this.tablesOrderableItemOutlets.forEach((item) => item.reset());
174
+
175
+ return dragState;
176
+ }
177
+
178
+ drop() {
179
+ // note: early returns guard against turbo updates that prevent us finding
180
+ // the right item to drop on. In this situation it's better to discard the
181
+ // drop than to drop in the wrong place.
182
+
183
+ const dragItem = this.dragItem;
184
+
185
+ if (!dragItem) return;
186
+
187
+ const newIndex = dragItem.dragIndex;
188
+ const targetItem = this.tablesOrderableItemOutlets[newIndex];
189
+
190
+ if (!targetItem) return;
191
+
192
+ // swap the dragged item into the correct position for its current offset
193
+ if (newIndex < dragItem.index) {
194
+ targetItem.row.insertAdjacentElement("beforebegin", dragItem.row);
195
+ } else if (newIndex > dragItem.index) {
196
+ targetItem.row.insertAdjacentElement("afterend", dragItem.row);
197
+ }
198
+
199
+ // reindex all items based on their new positions
200
+ this.tablesOrderableItemOutlets.forEach((item, index) =>
201
+ item.updateIndex(index)
202
+ );
203
+
204
+ // save the changes
205
+ this.commitChanges();
206
+ }
207
+
208
+ commitChanges() {
209
+ // clear any existing inputs to prevent duplicates
210
+ this.tablesOrderableFormOutlet.clear();
211
+
212
+ // insert any items that have changed position
213
+ this.tablesOrderableItemOutlets.forEach((item) => {
214
+ if (item.hasChanges) this.tablesOrderableFormOutlet.add(item);
215
+ });
216
+
217
+ this.tablesOrderableFormOutlet.submit();
218
+ }
219
+
220
+ //endregion
221
+
222
+ //region Events
223
+
224
+ mousedown(event) {
225
+ if (this.isDragging) return;
226
+
227
+ const target = this.#targetItem(event.target);
228
+
229
+ if (!target) return;
230
+
231
+ event.preventDefault(); // prevent built-in drag
232
+
233
+ this.startDragging(new DragState(this.element, event, target.id));
234
+
235
+ this.dragState.updateCursor(this.element, target.row, event, this.animate);
236
+ }
237
+
238
+ mousemove = (event) => {
239
+ if (!this.isDragging) return;
240
+
241
+ event.preventDefault(); // prevent build-in drag
242
+
243
+ if (this.ticking) return;
244
+
245
+ this.ticking = true;
246
+
247
+ window.requestAnimationFrame(() => {
248
+ this.ticking = false;
249
+ this.dragState.updateCursor(
250
+ this.element,
251
+ this.dragItem.row,
252
+ event,
253
+ this.animate
254
+ );
255
+ });
256
+ };
257
+
258
+ scroll = (event) => {
259
+ if (!this.isDragging || this.ticking) return;
260
+
261
+ this.ticking = true;
262
+
263
+ window.requestAnimationFrame(() => {
264
+ this.ticking = false;
265
+ this.dragState.updateScroll(
266
+ this.element,
267
+ this.dragItem.row,
268
+ this.animate
269
+ );
270
+ });
271
+ };
272
+
273
+ mouseup = (event) => {
274
+ if (!this.isDragging) return;
275
+
276
+ this.drop();
277
+ this.stopDragging();
278
+ this.tablesOrderableFormOutlets.forEach((form) => delete form.dragState);
279
+ };
280
+
281
+ tablesOrderableFormOutletConnected(form, element) {
282
+ if (form.dragState) {
283
+ // restore the previous controller's state
284
+ this.startDragging(form.dragState);
285
+ }
286
+ }
287
+
288
+ tablesOrderableFormOutletDisconnected(form, element) {
289
+ if (this.isDragging) {
290
+ // cache drag state in the form
291
+ form.dragState = this.stopDragging();
292
+ }
293
+ }
294
+
295
+ //endregion
296
+
297
+ //region Helpers
298
+
299
+ /**
300
+ * Updates the position of the drag item with a relative offset. Updates
301
+ * other items relative to the new position of the drag item, as required.
302
+ *
303
+ * @callback {OrderableListController~animate}
304
+ * @param {number} offset
305
+ */
306
+ animate = (offset) => {
307
+ const dragItem = this.dragItem;
308
+
309
+ // Visually update the dragItem so it follows the cursor
310
+ dragItem.dragUpdate(offset);
311
+
312
+ // Visually updates the position of all items in the list relative to the
313
+ // dragged item. No actual changes to orderings at this stage.
314
+ this.#currentItems.forEach((item, index) => {
315
+ if (item === dragItem) return;
316
+ item.updateVisually(index);
317
+ });
318
+ };
319
+
320
+ get isDragging() {
321
+ return !!this.dragState;
322
+ }
323
+
324
+ get dragItem() {
325
+ if (!this.isDragging) return null;
326
+
327
+ return this.tablesOrderableItemOutlets.find(
328
+ (item) => item.id === this.dragState.targetId
329
+ );
330
+ }
331
+
332
+ /**
333
+ * Returns the current items in the list, sorted by their current index.
334
+ * Current uses the drag index if the item is being dragged, if set.
335
+ *
336
+ * @returns {Array[OrderableRowController]}
337
+ */
338
+ get #currentItems() {
339
+ return this.tablesOrderableItemOutlets.toSorted(
340
+ (a, b) => a.comparisonIndex - b.comparisonIndex
341
+ );
342
+ }
343
+
344
+ /**
345
+ * Returns the item outlet that was clicked on, if any.
346
+ *
347
+ * @param element {HTMLElement} the clicked ordinal cell
348
+ * @returns {OrderableRowController}
349
+ */
350
+ #targetItem(element) {
351
+ return this.tablesOrderableItemOutlets.find(
352
+ (item) => item.element === element
353
+ );
354
+ }
355
+
356
+ //endregion
357
+ }
358
+
359
+ /**
360
+ * During drag we want to be able to translate a document-relative coordinate
361
+ * into a coordinate relative to the list element. This state object calculates
362
+ * and stores internal state so that we can translate absolute page coordinates
363
+ * from mouse events into relative offsets for the list items within the list
364
+ * element.
365
+ *
366
+ * We also keep track of the drag target so that if the controller is attached
367
+ * to a new element during the drag we can continue after the turbo update.
368
+ */
369
+ class DragState {
370
+ /**
371
+ * @param list {HTMLElement} the list controller's element (tbody)
372
+ * @param event {MouseEvent} the initial event
373
+ * @param id {String} the id of the element being dragged
374
+ */
375
+ constructor(list, event, id) {
376
+ // cursor offset is the offset of the cursor relative to the drag item
377
+ this.cursorOffset = event.offsetY;
378
+
379
+ // initial offset is the offset position of the drag item at drag start
380
+ this.initialPosition = event.target.offsetTop - list.offsetTop;
381
+
382
+ // id of the item being dragged
383
+ this.targetId = id;
384
+ }
385
+
386
+ /**
387
+ * Calculates the offset of the drag item relative to its initial position.
388
+ *
389
+ * @param list {HTMLElement} the list controller's element (tbody)
390
+ * @param row {HTMLElement} the row being dragged
391
+ * @param event {MouseEvent} the current event
392
+ * @param callback {OrderableListController~animate} updates the drag item with a relative offset
393
+ */
394
+ updateCursor(list, row, event, callback) {
395
+ // Calculate and store the list offset relative to the viewport
396
+ // This value is cached so we can calculate the outcome of any scroll events
397
+ this.listOffset = list.getBoundingClientRect().top;
398
+
399
+ // Calculate the position of the cursor relative to the list.
400
+ // Accounts for scroll offsets by using the item's bounding client rect.
401
+ const cursorPosition = event.clientY - this.listOffset;
402
+
403
+ // intended item position relative to the list, from cursor position
404
+ let itemPosition = cursorPosition - this.cursorOffset;
405
+
406
+ this.#updateItemPosition(list, row, itemPosition, callback);
407
+ }
408
+
409
+ /**
410
+ * Animates the item's position as the list scrolls. Requires a previous call
411
+ * to set the scroll offset.
412
+ *
413
+ * @param list {HTMLElement} the list controller's element (tbody)
414
+ * @param row {HTMLElement} the row being dragged
415
+ * @param callback {OrderableListController~animate} updates the drag item with a relative offset
416
+ */
417
+ updateScroll(list, row, callback) {
418
+ const previousScrollOffset = this.listOffset;
419
+
420
+ // Calculate and store the list offset relative to the viewport
421
+ // This value is cached so we can calculate the outcome of any scroll events
422
+ this.listOffset = list.getBoundingClientRect().top;
423
+
424
+ // Calculate the change in scroll offset since the last update
425
+ const scrollDelta = previousScrollOffset - this.listOffset;
426
+
427
+ // intended item position relative to the list, from cursor position
428
+ const position = this.position + scrollDelta;
429
+
430
+ this.#updateItemPosition(list, row, position, callback);
431
+ }
432
+
433
+ #updateItemPosition(list, row, position, callback) {
434
+ // ensure itemPosition is within the bounds of the list (tbody)
435
+ position = Math.max(position, 0);
436
+ position = Math.min(position, list.offsetHeight - row.offsetHeight);
437
+
438
+ // cache the item's position relative to the list for use in scroll events
439
+ this.position = position;
440
+
441
+ // Item has position: relative, so we want to calculate the amount to move
442
+ // the item relative to it's DOM position to represent how much it has been
443
+ // dragged by.
444
+ const offset = position - this.initialPosition;
445
+
446
+ // Convert itemPosition from offset relative to list to offset relative to
447
+ // its position within the DOM (if it hadn't moved).
448
+ callback(offset);
449
+ }
450
+ }
451
+
452
+ class OrderableFormController extends Controller {
453
+ add(item) {
454
+ const { id_name, id_value, index_name } = item.paramsValue;
455
+ this.element.insertAdjacentHTML(
456
+ "beforeend",
457
+ `<input type="hidden" name="${id_name}" value="${id_value}" data-generated>
458
+ <input type="hidden" name="${index_name}" value="${item.index}" data-generated>`
459
+ );
460
+ }
461
+
462
+ submit() {
463
+ if (this.inputs.length === 0) return;
464
+
465
+ this.element.requestSubmit();
466
+ }
467
+
468
+ clear() {
469
+ this.inputs.forEach((input) => input.remove());
470
+ }
471
+
472
+ get inputs() {
473
+ return this.element.querySelectorAll("input[data-generated]");
474
+ }
475
+ }
476
+
477
+ const Definitions = [
478
+ {
479
+ identifier: "tables--turbo--collection",
480
+ controllerConstructor: TurboCollectionController,
481
+ },
482
+ {
483
+ identifier: "tables--orderable--item",
484
+ controllerConstructor: OrderableRowController,
485
+ },
486
+ {
487
+ identifier: "tables--orderable--list",
488
+ controllerConstructor: OrderableListController,
489
+ },
490
+ {
491
+ identifier: "tables--orderable--form",
492
+ controllerConstructor: OrderableFormController,
493
+ },
494
+ ];
495
+
496
+ export { Definitions as default };
@@ -0,0 +1,496 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ import { Turbo } from '@hotwired/turbo-rails';
3
+
4
+ class TurboCollectionController extends Controller {
5
+ static values = {
6
+ query: String,
7
+ sort: String,
8
+ };
9
+
10
+ queryValueChanged(query) {
11
+ Turbo.navigator.history.replace(this.#url(query));
12
+ }
13
+
14
+ sortValueChanged(sort) {
15
+ document.querySelectorAll(this.#sortSelector).forEach((input) => {
16
+ if (input) input.value = sort;
17
+ });
18
+ }
19
+
20
+ get #sortSelector() {
21
+ return "input[name='sort']";
22
+ }
23
+
24
+ #url(query) {
25
+ const frame = this.element.closest("turbo-frame");
26
+ let url;
27
+
28
+ if (frame) {
29
+ url = new URL(frame.baseURI);
30
+ } else {
31
+ url = new URL(window.location.href);
32
+ }
33
+
34
+ url.search = query;
35
+
36
+ return url;
37
+ }
38
+ }
39
+
40
+ class OrderableRowController extends Controller {
41
+ static values = {
42
+ params: Object,
43
+ };
44
+
45
+ connect() {
46
+ // index from server may be inconsistent with the visual ordering,
47
+ // especially if this is a new node. Use positional indexes instead,
48
+ // as these are the values we will send on save.
49
+ this.index = domIndex(this.row);
50
+ }
51
+
52
+ paramsValueChanged(params) {
53
+ this.id = params.id_value;
54
+ }
55
+
56
+ dragUpdate(offset) {
57
+ this.dragOffset = offset;
58
+ this.row.style.position = "relative";
59
+ this.row.style.top = offset + "px";
60
+ this.row.style.zIndex = "1";
61
+ this.row.toggleAttribute("dragging", true);
62
+ }
63
+
64
+ /**
65
+ * Called on items that are not the dragged item during drag. Updates the
66
+ * visual position of the item relative to the dragged item.
67
+ *
68
+ * @param index {number} intended index of the item during drag
69
+ */
70
+ updateVisually(index) {
71
+ this.row.style.position = "relative";
72
+ this.row.style.top = `${
73
+ this.row.offsetHeight * (index - this.dragIndex)
74
+ }px`;
75
+ }
76
+
77
+ /**
78
+ * Set the index value of the item. This is called on all items after a drop
79
+ * event. If the index is different to the params index then this item has
80
+ * changed.
81
+ *
82
+ * @param index {number} the new index value
83
+ */
84
+ updateIndex(index) {
85
+ this.index = index;
86
+ }
87
+
88
+ /**
89
+ * Restore any visual changes made during drag and remove the drag state.
90
+ */
91
+ reset() {
92
+ delete this.dragOffset;
93
+ this.row.removeAttribute("style");
94
+ this.row.removeAttribute("dragging");
95
+ }
96
+
97
+ /**
98
+ * @returns {boolean} true when the item has a change to its index value
99
+ */
100
+ get hasChanges() {
101
+ return this.paramsValue.index_value !== this.index;
102
+ }
103
+
104
+ /**
105
+ * Calculate the relative index of the item during drag. This is used to
106
+ * sort items during drag as it takes into account any uncommitted changes
107
+ * to index caused by the drag offset.
108
+ *
109
+ * @returns {number} index for the purposes of drag and drop ordering
110
+ */
111
+ get dragIndex() {
112
+ if (this.dragOffset && this.dragOffset !== 0) {
113
+ return this.index + Math.round(this.dragOffset / this.row.offsetHeight);
114
+ } else {
115
+ return this.index;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Index value for use in comparisons during drag. This is used to determine
121
+ * whether the dragged item is above or below another item. If this item is
122
+ * being dragged then we offset the index by 0.5 to ensure that it jumps up
123
+ * or down when it reaches the midpoint of the item above or below it.
124
+ *
125
+ * @returns {number}
126
+ */
127
+ get comparisonIndex() {
128
+ if (this.dragOffset) {
129
+ return this.dragIndex + (this.dragOffset > 0 ? 0.5 : -0.5);
130
+ } else {
131
+ return this.index;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * The containing row element.
137
+ *
138
+ * @returns {HTMLElement}
139
+ */
140
+ get row() {
141
+ return this.element.parentElement;
142
+ }
143
+ }
144
+
145
+ function domIndex(element) {
146
+ return Array.from(element.parentElement.children).indexOf(element);
147
+ }
148
+
149
+ class OrderableListController extends Controller {
150
+ static outlets = ["tables--orderable--item", "tables--orderable--form"];
151
+
152
+ //region State transitions
153
+
154
+ startDragging(dragState) {
155
+ this.dragState = dragState;
156
+
157
+ document.addEventListener("mousemove", this.mousemove);
158
+ document.addEventListener("mouseup", this.mouseup);
159
+ window.addEventListener("scroll", this.scroll, true);
160
+
161
+ this.element.style.position = "relative";
162
+ }
163
+
164
+ stopDragging() {
165
+ const dragState = this.dragState;
166
+ delete this.dragState;
167
+
168
+ document.removeEventListener("mousemove", this.mousemove);
169
+ document.removeEventListener("mouseup", this.mouseup);
170
+ window.removeEventListener("scroll", this.scroll, true);
171
+
172
+ this.element.removeAttribute("style");
173
+ this.tablesOrderableItemOutlets.forEach((item) => item.reset());
174
+
175
+ return dragState;
176
+ }
177
+
178
+ drop() {
179
+ // note: early returns guard against turbo updates that prevent us finding
180
+ // the right item to drop on. In this situation it's better to discard the
181
+ // drop than to drop in the wrong place.
182
+
183
+ const dragItem = this.dragItem;
184
+
185
+ if (!dragItem) return;
186
+
187
+ const newIndex = dragItem.dragIndex;
188
+ const targetItem = this.tablesOrderableItemOutlets[newIndex];
189
+
190
+ if (!targetItem) return;
191
+
192
+ // swap the dragged item into the correct position for its current offset
193
+ if (newIndex < dragItem.index) {
194
+ targetItem.row.insertAdjacentElement("beforebegin", dragItem.row);
195
+ } else if (newIndex > dragItem.index) {
196
+ targetItem.row.insertAdjacentElement("afterend", dragItem.row);
197
+ }
198
+
199
+ // reindex all items based on their new positions
200
+ this.tablesOrderableItemOutlets.forEach((item, index) =>
201
+ item.updateIndex(index)
202
+ );
203
+
204
+ // save the changes
205
+ this.commitChanges();
206
+ }
207
+
208
+ commitChanges() {
209
+ // clear any existing inputs to prevent duplicates
210
+ this.tablesOrderableFormOutlet.clear();
211
+
212
+ // insert any items that have changed position
213
+ this.tablesOrderableItemOutlets.forEach((item) => {
214
+ if (item.hasChanges) this.tablesOrderableFormOutlet.add(item);
215
+ });
216
+
217
+ this.tablesOrderableFormOutlet.submit();
218
+ }
219
+
220
+ //endregion
221
+
222
+ //region Events
223
+
224
+ mousedown(event) {
225
+ if (this.isDragging) return;
226
+
227
+ const target = this.#targetItem(event.target);
228
+
229
+ if (!target) return;
230
+
231
+ event.preventDefault(); // prevent built-in drag
232
+
233
+ this.startDragging(new DragState(this.element, event, target.id));
234
+
235
+ this.dragState.updateCursor(this.element, target.row, event, this.animate);
236
+ }
237
+
238
+ mousemove = (event) => {
239
+ if (!this.isDragging) return;
240
+
241
+ event.preventDefault(); // prevent build-in drag
242
+
243
+ if (this.ticking) return;
244
+
245
+ this.ticking = true;
246
+
247
+ window.requestAnimationFrame(() => {
248
+ this.ticking = false;
249
+ this.dragState.updateCursor(
250
+ this.element,
251
+ this.dragItem.row,
252
+ event,
253
+ this.animate
254
+ );
255
+ });
256
+ };
257
+
258
+ scroll = (event) => {
259
+ if (!this.isDragging || this.ticking) return;
260
+
261
+ this.ticking = true;
262
+
263
+ window.requestAnimationFrame(() => {
264
+ this.ticking = false;
265
+ this.dragState.updateScroll(
266
+ this.element,
267
+ this.dragItem.row,
268
+ this.animate
269
+ );
270
+ });
271
+ };
272
+
273
+ mouseup = (event) => {
274
+ if (!this.isDragging) return;
275
+
276
+ this.drop();
277
+ this.stopDragging();
278
+ this.tablesOrderableFormOutlets.forEach((form) => delete form.dragState);
279
+ };
280
+
281
+ tablesOrderableFormOutletConnected(form, element) {
282
+ if (form.dragState) {
283
+ // restore the previous controller's state
284
+ this.startDragging(form.dragState);
285
+ }
286
+ }
287
+
288
+ tablesOrderableFormOutletDisconnected(form, element) {
289
+ if (this.isDragging) {
290
+ // cache drag state in the form
291
+ form.dragState = this.stopDragging();
292
+ }
293
+ }
294
+
295
+ //endregion
296
+
297
+ //region Helpers
298
+
299
+ /**
300
+ * Updates the position of the drag item with a relative offset. Updates
301
+ * other items relative to the new position of the drag item, as required.
302
+ *
303
+ * @callback {OrderableListController~animate}
304
+ * @param {number} offset
305
+ */
306
+ animate = (offset) => {
307
+ const dragItem = this.dragItem;
308
+
309
+ // Visually update the dragItem so it follows the cursor
310
+ dragItem.dragUpdate(offset);
311
+
312
+ // Visually updates the position of all items in the list relative to the
313
+ // dragged item. No actual changes to orderings at this stage.
314
+ this.#currentItems.forEach((item, index) => {
315
+ if (item === dragItem) return;
316
+ item.updateVisually(index);
317
+ });
318
+ };
319
+
320
+ get isDragging() {
321
+ return !!this.dragState;
322
+ }
323
+
324
+ get dragItem() {
325
+ if (!this.isDragging) return null;
326
+
327
+ return this.tablesOrderableItemOutlets.find(
328
+ (item) => item.id === this.dragState.targetId
329
+ );
330
+ }
331
+
332
+ /**
333
+ * Returns the current items in the list, sorted by their current index.
334
+ * Current uses the drag index if the item is being dragged, if set.
335
+ *
336
+ * @returns {Array[OrderableRowController]}
337
+ */
338
+ get #currentItems() {
339
+ return this.tablesOrderableItemOutlets.toSorted(
340
+ (a, b) => a.comparisonIndex - b.comparisonIndex
341
+ );
342
+ }
343
+
344
+ /**
345
+ * Returns the item outlet that was clicked on, if any.
346
+ *
347
+ * @param element {HTMLElement} the clicked ordinal cell
348
+ * @returns {OrderableRowController}
349
+ */
350
+ #targetItem(element) {
351
+ return this.tablesOrderableItemOutlets.find(
352
+ (item) => item.element === element
353
+ );
354
+ }
355
+
356
+ //endregion
357
+ }
358
+
359
+ /**
360
+ * During drag we want to be able to translate a document-relative coordinate
361
+ * into a coordinate relative to the list element. This state object calculates
362
+ * and stores internal state so that we can translate absolute page coordinates
363
+ * from mouse events into relative offsets for the list items within the list
364
+ * element.
365
+ *
366
+ * We also keep track of the drag target so that if the controller is attached
367
+ * to a new element during the drag we can continue after the turbo update.
368
+ */
369
+ class DragState {
370
+ /**
371
+ * @param list {HTMLElement} the list controller's element (tbody)
372
+ * @param event {MouseEvent} the initial event
373
+ * @param id {String} the id of the element being dragged
374
+ */
375
+ constructor(list, event, id) {
376
+ // cursor offset is the offset of the cursor relative to the drag item
377
+ this.cursorOffset = event.offsetY;
378
+
379
+ // initial offset is the offset position of the drag item at drag start
380
+ this.initialPosition = event.target.offsetTop - list.offsetTop;
381
+
382
+ // id of the item being dragged
383
+ this.targetId = id;
384
+ }
385
+
386
+ /**
387
+ * Calculates the offset of the drag item relative to its initial position.
388
+ *
389
+ * @param list {HTMLElement} the list controller's element (tbody)
390
+ * @param row {HTMLElement} the row being dragged
391
+ * @param event {MouseEvent} the current event
392
+ * @param callback {OrderableListController~animate} updates the drag item with a relative offset
393
+ */
394
+ updateCursor(list, row, event, callback) {
395
+ // Calculate and store the list offset relative to the viewport
396
+ // This value is cached so we can calculate the outcome of any scroll events
397
+ this.listOffset = list.getBoundingClientRect().top;
398
+
399
+ // Calculate the position of the cursor relative to the list.
400
+ // Accounts for scroll offsets by using the item's bounding client rect.
401
+ const cursorPosition = event.clientY - this.listOffset;
402
+
403
+ // intended item position relative to the list, from cursor position
404
+ let itemPosition = cursorPosition - this.cursorOffset;
405
+
406
+ this.#updateItemPosition(list, row, itemPosition, callback);
407
+ }
408
+
409
+ /**
410
+ * Animates the item's position as the list scrolls. Requires a previous call
411
+ * to set the scroll offset.
412
+ *
413
+ * @param list {HTMLElement} the list controller's element (tbody)
414
+ * @param row {HTMLElement} the row being dragged
415
+ * @param callback {OrderableListController~animate} updates the drag item with a relative offset
416
+ */
417
+ updateScroll(list, row, callback) {
418
+ const previousScrollOffset = this.listOffset;
419
+
420
+ // Calculate and store the list offset relative to the viewport
421
+ // This value is cached so we can calculate the outcome of any scroll events
422
+ this.listOffset = list.getBoundingClientRect().top;
423
+
424
+ // Calculate the change in scroll offset since the last update
425
+ const scrollDelta = previousScrollOffset - this.listOffset;
426
+
427
+ // intended item position relative to the list, from cursor position
428
+ const position = this.position + scrollDelta;
429
+
430
+ this.#updateItemPosition(list, row, position, callback);
431
+ }
432
+
433
+ #updateItemPosition(list, row, position, callback) {
434
+ // ensure itemPosition is within the bounds of the list (tbody)
435
+ position = Math.max(position, 0);
436
+ position = Math.min(position, list.offsetHeight - row.offsetHeight);
437
+
438
+ // cache the item's position relative to the list for use in scroll events
439
+ this.position = position;
440
+
441
+ // Item has position: relative, so we want to calculate the amount to move
442
+ // the item relative to it's DOM position to represent how much it has been
443
+ // dragged by.
444
+ const offset = position - this.initialPosition;
445
+
446
+ // Convert itemPosition from offset relative to list to offset relative to
447
+ // its position within the DOM (if it hadn't moved).
448
+ callback(offset);
449
+ }
450
+ }
451
+
452
+ class OrderableFormController extends Controller {
453
+ add(item) {
454
+ const { id_name, id_value, index_name } = item.paramsValue;
455
+ this.element.insertAdjacentHTML(
456
+ "beforeend",
457
+ `<input type="hidden" name="${id_name}" value="${id_value}" data-generated>
458
+ <input type="hidden" name="${index_name}" value="${item.index}" data-generated>`
459
+ );
460
+ }
461
+
462
+ submit() {
463
+ if (this.inputs.length === 0) return;
464
+
465
+ this.element.requestSubmit();
466
+ }
467
+
468
+ clear() {
469
+ this.inputs.forEach((input) => input.remove());
470
+ }
471
+
472
+ get inputs() {
473
+ return this.element.querySelectorAll("input[data-generated]");
474
+ }
475
+ }
476
+
477
+ const Definitions = [
478
+ {
479
+ identifier: "tables--turbo--collection",
480
+ controllerConstructor: TurboCollectionController,
481
+ },
482
+ {
483
+ identifier: "tables--orderable--item",
484
+ controllerConstructor: OrderableRowController,
485
+ },
486
+ {
487
+ identifier: "tables--orderable--list",
488
+ controllerConstructor: OrderableListController,
489
+ },
490
+ {
491
+ identifier: "tables--orderable--form",
492
+ controllerConstructor: OrderableFormController,
493
+ },
494
+ ];
495
+
496
+ export { Definitions as default };
@@ -0,0 +1,2 @@
1
+ import{Controller as t}from"@hotwired/stimulus";import{Turbo as e}from"@hotwired/turbo-rails";class s{constructor(t,e,s){this.cursorOffset=e.offsetY,this.initialPosition=e.target.offsetTop-t.offsetTop,this.targetId=s}updateCursor(t,e,s,i){this.listOffset=t.getBoundingClientRect().top;let r=s.clientY-this.listOffset-this.cursorOffset;this.#t(t,e,r,i)}updateScroll(t,e,s){const i=this.listOffset;this.listOffset=t.getBoundingClientRect().top;const r=i-this.listOffset,a=this.position+r;this.#t(t,e,a,s)}#t(t,e,s,i){s=Math.max(s,0),s=Math.min(s,t.offsetHeight-e.offsetHeight),this.position=s;i(s-this.initialPosition)}}const i=[{identifier:"tables--turbo--collection",controllerConstructor:class extends t{static values={query:String,sort:String};queryValueChanged(t){e.navigator.history.replace(this.#e(t))}sortValueChanged(t){document.querySelectorAll(this.#s).forEach((e=>{e&&(e.value=t)}))}get#s(){return"input[name='sort']"}#e(t){const e=this.element.closest("turbo-frame");let s;return s=e?new URL(e.baseURI):new URL(window.location.href),s.search=t,s}}},{identifier:"tables--orderable--item",controllerConstructor:class extends t{static values={params:Object};connect(){var t;this.index=(t=this.row,Array.from(t.parentElement.children).indexOf(t))}paramsValueChanged(t){this.id=t.id_value}dragUpdate(t){this.dragOffset=t,this.row.style.position="relative",this.row.style.top=t+"px",this.row.style.zIndex="1",this.row.toggleAttribute("dragging",!0)}updateVisually(t){this.row.style.position="relative",this.row.style.top=this.row.offsetHeight*(t-this.dragIndex)+"px"}updateIndex(t){this.index=t}reset(){delete this.dragOffset,this.row.removeAttribute("style"),this.row.removeAttribute("dragging")}get hasChanges(){return this.paramsValue.index_value!==this.index}get dragIndex(){return this.dragOffset&&0!==this.dragOffset?this.index+Math.round(this.dragOffset/this.row.offsetHeight):this.index}get comparisonIndex(){return this.dragOffset?this.dragIndex+(this.dragOffset>0?.5:-.5):this.index}get row(){return this.element.parentElement}}},{identifier:"tables--orderable--list",controllerConstructor:class extends t{static outlets=["tables--orderable--item","tables--orderable--form"];startDragging(t){this.dragState=t,document.addEventListener("mousemove",this.mousemove),document.addEventListener("mouseup",this.mouseup),window.addEventListener("scroll",this.scroll,!0),this.element.style.position="relative"}stopDragging(){const t=this.dragState;return delete this.dragState,document.removeEventListener("mousemove",this.mousemove),document.removeEventListener("mouseup",this.mouseup),window.removeEventListener("scroll",this.scroll,!0),this.element.removeAttribute("style"),this.tablesOrderableItemOutlets.forEach((t=>t.reset())),t}drop(){const t=this.dragItem;if(!t)return;const e=t.dragIndex,s=this.tablesOrderableItemOutlets[e];s&&(e<t.index?s.row.insertAdjacentElement("beforebegin",t.row):e>t.index&&s.row.insertAdjacentElement("afterend",t.row),this.tablesOrderableItemOutlets.forEach(((t,e)=>t.updateIndex(e))),this.commitChanges())}commitChanges(){this.tablesOrderableFormOutlet.clear(),this.tablesOrderableItemOutlets.forEach((t=>{t.hasChanges&&this.tablesOrderableFormOutlet.add(t)})),this.tablesOrderableFormOutlet.submit()}mousedown(t){if(this.isDragging)return;const e=this.#i(t.target);e&&(t.preventDefault(),this.startDragging(new s(this.element,t,e.id)),this.dragState.updateCursor(this.element,e.row,t,this.animate))}mousemove=t=>{this.isDragging&&(t.preventDefault(),this.ticking||(this.ticking=!0,window.requestAnimationFrame((()=>{this.ticking=!1,this.dragState.updateCursor(this.element,this.dragItem.row,t,this.animate)}))))};scroll=t=>{this.isDragging&&!this.ticking&&(this.ticking=!0,window.requestAnimationFrame((()=>{this.ticking=!1,this.dragState.updateScroll(this.element,this.dragItem.row,this.animate)})))};mouseup=t=>{this.isDragging&&(this.drop(),this.stopDragging(),this.tablesOrderableFormOutlets.forEach((t=>delete t.dragState)))};tablesOrderableFormOutletConnected(t,e){t.dragState&&this.startDragging(t.dragState)}tablesOrderableFormOutletDisconnected(t,e){this.isDragging&&(t.dragState=this.stopDragging())}animate=t=>{const e=this.dragItem;e.dragUpdate(t),this.#r.forEach(((t,s)=>{t!==e&&t.updateVisually(s)}))};get isDragging(){return!!this.dragState}get dragItem(){return this.isDragging?this.tablesOrderableItemOutlets.find((t=>t.id===this.dragState.targetId)):null}get#r(){return this.tablesOrderableItemOutlets.toSorted(((t,e)=>t.comparisonIndex-e.comparisonIndex))}#i(t){return this.tablesOrderableItemOutlets.find((e=>e.element===t))}}},{identifier:"tables--orderable--form",controllerConstructor:class extends t{add(t){const{id_name:e,id_value:s,index_name:i}=t.paramsValue;this.element.insertAdjacentHTML("beforeend",`<input type="hidden" name="${e}" value="${s}" data-generated>\n <input type="hidden" name="${i}" value="${t.index}" data-generated>`)}submit(){0!==this.inputs.length&&this.element.requestSubmit()}clear(){this.inputs.forEach((t=>t.remove()))}get inputs(){return this.element.querySelectorAll("input[data-generated]")}}}];export{i as default};
2
+ //# sourceMappingURL=tables.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.min.js","sources":["../../../javascript/tables/orderable/list_controller.js","../../../javascript/tables/application.js","../../../javascript/tables/turbo/collection_controller.js","../../../javascript/tables/orderable/item_controller.js","../../../javascript/tables/orderable/form_controller.js"],"sourcesContent":["import { Controller } from \"@hotwired/stimulus\";\n\nexport default class OrderableListController extends Controller {\n static outlets = [\"tables--orderable--item\", \"tables--orderable--form\"];\n\n //region State transitions\n\n startDragging(dragState) {\n this.dragState = dragState;\n\n document.addEventListener(\"mousemove\", this.mousemove);\n document.addEventListener(\"mouseup\", this.mouseup);\n window.addEventListener(\"scroll\", this.scroll, true);\n\n this.element.style.position = \"relative\";\n }\n\n stopDragging() {\n const dragState = this.dragState;\n delete this.dragState;\n\n document.removeEventListener(\"mousemove\", this.mousemove);\n document.removeEventListener(\"mouseup\", this.mouseup);\n window.removeEventListener(\"scroll\", this.scroll, true);\n\n this.element.removeAttribute(\"style\");\n this.tablesOrderableItemOutlets.forEach((item) => item.reset());\n\n return dragState;\n }\n\n drop() {\n // note: early returns guard against turbo updates that prevent us finding\n // the right item to drop on. In this situation it's better to discard the\n // drop than to drop in the wrong place.\n\n const dragItem = this.dragItem;\n\n if (!dragItem) return;\n\n const newIndex = dragItem.dragIndex;\n const targetItem = this.tablesOrderableItemOutlets[newIndex];\n\n if (!targetItem) return;\n\n // swap the dragged item into the correct position for its current offset\n if (newIndex < dragItem.index) {\n targetItem.row.insertAdjacentElement(\"beforebegin\", dragItem.row);\n } else if (newIndex > dragItem.index) {\n targetItem.row.insertAdjacentElement(\"afterend\", dragItem.row);\n }\n\n // reindex all items based on their new positions\n this.tablesOrderableItemOutlets.forEach((item, index) =>\n item.updateIndex(index)\n );\n\n // save the changes\n this.commitChanges();\n }\n\n commitChanges() {\n // clear any existing inputs to prevent duplicates\n this.tablesOrderableFormOutlet.clear();\n\n // insert any items that have changed position\n this.tablesOrderableItemOutlets.forEach((item) => {\n if (item.hasChanges) this.tablesOrderableFormOutlet.add(item);\n });\n\n this.tablesOrderableFormOutlet.submit();\n }\n\n //endregion\n\n //region Events\n\n mousedown(event) {\n if (this.isDragging) return;\n\n const target = this.#targetItem(event.target);\n\n if (!target) return;\n\n event.preventDefault(); // prevent built-in drag\n\n this.startDragging(new DragState(this.element, event, target.id));\n\n this.dragState.updateCursor(this.element, target.row, event, this.animate);\n }\n\n mousemove = (event) => {\n if (!this.isDragging) return;\n\n event.preventDefault(); // prevent build-in drag\n\n if (this.ticking) return;\n\n this.ticking = true;\n\n window.requestAnimationFrame(() => {\n this.ticking = false;\n this.dragState.updateCursor(\n this.element,\n this.dragItem.row,\n event,\n this.animate\n );\n });\n };\n\n scroll = (event) => {\n if (!this.isDragging || this.ticking) return;\n\n this.ticking = true;\n\n window.requestAnimationFrame(() => {\n this.ticking = false;\n this.dragState.updateScroll(\n this.element,\n this.dragItem.row,\n this.animate\n );\n });\n };\n\n mouseup = (event) => {\n if (!this.isDragging) return;\n\n this.drop();\n this.stopDragging();\n this.tablesOrderableFormOutlets.forEach((form) => delete form.dragState);\n };\n\n tablesOrderableFormOutletConnected(form, element) {\n if (form.dragState) {\n // restore the previous controller's state\n this.startDragging(form.dragState);\n }\n }\n\n tablesOrderableFormOutletDisconnected(form, element) {\n if (this.isDragging) {\n // cache drag state in the form\n form.dragState = this.stopDragging();\n }\n }\n\n //endregion\n\n //region Helpers\n\n /**\n * Updates the position of the drag item with a relative offset. Updates\n * other items relative to the new position of the drag item, as required.\n *\n * @callback {OrderableListController~animate}\n * @param {number} offset\n */\n animate = (offset) => {\n const dragItem = this.dragItem;\n\n // Visually update the dragItem so it follows the cursor\n dragItem.dragUpdate(offset);\n\n // Visually updates the position of all items in the list relative to the\n // dragged item. No actual changes to orderings at this stage.\n this.#currentItems.forEach((item, index) => {\n if (item === dragItem) return;\n item.updateVisually(index);\n });\n };\n\n get isDragging() {\n return !!this.dragState;\n }\n\n get dragItem() {\n if (!this.isDragging) return null;\n\n return this.tablesOrderableItemOutlets.find(\n (item) => item.id === this.dragState.targetId\n );\n }\n\n /**\n * Returns the current items in the list, sorted by their current index.\n * Current uses the drag index if the item is being dragged, if set.\n *\n * @returns {Array[OrderableRowController]}\n */\n get #currentItems() {\n return this.tablesOrderableItemOutlets.toSorted(\n (a, b) => a.comparisonIndex - b.comparisonIndex\n );\n }\n\n /**\n * Returns the item outlet that was clicked on, if any.\n *\n * @param element {HTMLElement} the clicked ordinal cell\n * @returns {OrderableRowController}\n */\n #targetItem(element) {\n return this.tablesOrderableItemOutlets.find(\n (item) => item.element === element\n );\n }\n\n //endregion\n}\n\n/**\n * During drag we want to be able to translate a document-relative coordinate\n * into a coordinate relative to the list element. This state object calculates\n * and stores internal state so that we can translate absolute page coordinates\n * from mouse events into relative offsets for the list items within the list\n * element.\n *\n * We also keep track of the drag target so that if the controller is attached\n * to a new element during the drag we can continue after the turbo update.\n */\nclass DragState {\n /**\n * @param list {HTMLElement} the list controller's element (tbody)\n * @param event {MouseEvent} the initial event\n * @param id {String} the id of the element being dragged\n */\n constructor(list, event, id) {\n // cursor offset is the offset of the cursor relative to the drag item\n this.cursorOffset = event.offsetY;\n\n // initial offset is the offset position of the drag item at drag start\n this.initialPosition = event.target.offsetTop - list.offsetTop;\n\n // id of the item being dragged\n this.targetId = id;\n }\n\n /**\n * Calculates the offset of the drag item relative to its initial position.\n *\n * @param list {HTMLElement} the list controller's element (tbody)\n * @param row {HTMLElement} the row being dragged\n * @param event {MouseEvent} the current event\n * @param callback {OrderableListController~animate} updates the drag item with a relative offset\n */\n updateCursor(list, row, event, callback) {\n // Calculate and store the list offset relative to the viewport\n // This value is cached so we can calculate the outcome of any scroll events\n this.listOffset = list.getBoundingClientRect().top;\n\n // Calculate the position of the cursor relative to the list.\n // Accounts for scroll offsets by using the item's bounding client rect.\n const cursorPosition = event.clientY - this.listOffset;\n\n // intended item position relative to the list, from cursor position\n let itemPosition = cursorPosition - this.cursorOffset;\n\n this.#updateItemPosition(list, row, itemPosition, callback);\n }\n\n /**\n * Animates the item's position as the list scrolls. Requires a previous call\n * to set the scroll offset.\n *\n * @param list {HTMLElement} the list controller's element (tbody)\n * @param row {HTMLElement} the row being dragged\n * @param callback {OrderableListController~animate} updates the drag item with a relative offset\n */\n updateScroll(list, row, callback) {\n const previousScrollOffset = this.listOffset;\n\n // Calculate and store the list offset relative to the viewport\n // This value is cached so we can calculate the outcome of any scroll events\n this.listOffset = list.getBoundingClientRect().top;\n\n // Calculate the change in scroll offset since the last update\n const scrollDelta = previousScrollOffset - this.listOffset;\n\n // intended item position relative to the list, from cursor position\n const position = this.position + scrollDelta;\n\n this.#updateItemPosition(list, row, position, callback);\n }\n\n #updateItemPosition(list, row, position, callback) {\n // ensure itemPosition is within the bounds of the list (tbody)\n position = Math.max(position, 0);\n position = Math.min(position, list.offsetHeight - row.offsetHeight);\n\n // cache the item's position relative to the list for use in scroll events\n this.position = position;\n\n // Item has position: relative, so we want to calculate the amount to move\n // the item relative to it's DOM position to represent how much it has been\n // dragged by.\n const offset = position - this.initialPosition;\n\n // Convert itemPosition from offset relative to list to offset relative to\n // its position within the DOM (if it hadn't moved).\n callback(offset);\n }\n}\n","import TurboCollectionController from \"./turbo/collection_controller\";\nimport ItemController from \"./orderable/item_controller\";\nimport ListController from \"./orderable/list_controller\";\nimport FormController from \"./orderable/form_controller\";\n\nconst Definitions = [\n {\n identifier: \"tables--turbo--collection\",\n controllerConstructor: TurboCollectionController,\n },\n {\n identifier: \"tables--orderable--item\",\n controllerConstructor: ItemController,\n },\n {\n identifier: \"tables--orderable--list\",\n controllerConstructor: ListController,\n },\n {\n identifier: \"tables--orderable--form\",\n controllerConstructor: FormController,\n },\n];\n\nexport { Definitions as default };\n","import { Controller } from \"@hotwired/stimulus\";\nimport { Turbo } from \"@hotwired/turbo-rails\";\n\nexport default class TurboCollectionController extends Controller {\n static values = {\n query: String,\n sort: String,\n };\n\n queryValueChanged(query) {\n Turbo.navigator.history.replace(this.#url(query));\n }\n\n sortValueChanged(sort) {\n document.querySelectorAll(this.#sortSelector).forEach((input) => {\n if (input) input.value = sort;\n });\n }\n\n get #sortSelector() {\n return \"input[name='sort']\";\n }\n\n #url(query) {\n const frame = this.element.closest(\"turbo-frame\");\n let url;\n\n if (frame) {\n url = new URL(frame.baseURI);\n } else {\n url = new URL(window.location.href);\n }\n\n url.search = query;\n\n return url;\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class OrderableRowController extends Controller {\n static values = {\n params: Object,\n };\n\n connect() {\n // index from server may be inconsistent with the visual ordering,\n // especially if this is a new node. Use positional indexes instead,\n // as these are the values we will send on save.\n this.index = domIndex(this.row);\n }\n\n paramsValueChanged(params) {\n this.id = params.id_value;\n }\n\n dragUpdate(offset) {\n this.dragOffset = offset;\n this.row.style.position = \"relative\";\n this.row.style.top = offset + \"px\";\n this.row.style.zIndex = \"1\";\n this.row.toggleAttribute(\"dragging\", true);\n }\n\n /**\n * Called on items that are not the dragged item during drag. Updates the\n * visual position of the item relative to the dragged item.\n *\n * @param index {number} intended index of the item during drag\n */\n updateVisually(index) {\n this.row.style.position = \"relative\";\n this.row.style.top = `${\n this.row.offsetHeight * (index - this.dragIndex)\n }px`;\n }\n\n /**\n * Set the index value of the item. This is called on all items after a drop\n * event. If the index is different to the params index then this item has\n * changed.\n *\n * @param index {number} the new index value\n */\n updateIndex(index) {\n this.index = index;\n }\n\n /**\n * Restore any visual changes made during drag and remove the drag state.\n */\n reset() {\n delete this.dragOffset;\n this.row.removeAttribute(\"style\");\n this.row.removeAttribute(\"dragging\");\n }\n\n /**\n * @returns {boolean} true when the item has a change to its index value\n */\n get hasChanges() {\n return this.paramsValue.index_value !== this.index;\n }\n\n /**\n * Calculate the relative index of the item during drag. This is used to\n * sort items during drag as it takes into account any uncommitted changes\n * to index caused by the drag offset.\n *\n * @returns {number} index for the purposes of drag and drop ordering\n */\n get dragIndex() {\n if (this.dragOffset && this.dragOffset !== 0) {\n return this.index + Math.round(this.dragOffset / this.row.offsetHeight);\n } else {\n return this.index;\n }\n }\n\n /**\n * Index value for use in comparisons during drag. This is used to determine\n * whether the dragged item is above or below another item. If this item is\n * being dragged then we offset the index by 0.5 to ensure that it jumps up\n * or down when it reaches the midpoint of the item above or below it.\n *\n * @returns {number}\n */\n get comparisonIndex() {\n if (this.dragOffset) {\n return this.dragIndex + (this.dragOffset > 0 ? 0.5 : -0.5);\n } else {\n return this.index;\n }\n }\n\n /**\n * The containing row element.\n *\n * @returns {HTMLElement}\n */\n get row() {\n return this.element.parentElement;\n }\n}\n\nfunction domIndex(element) {\n return Array.from(element.parentElement.children).indexOf(element);\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class OrderableFormController extends Controller {\n add(item) {\n const { id_name, id_value, index_name } = item.paramsValue;\n this.element.insertAdjacentHTML(\n \"beforeend\",\n `<input type=\"hidden\" name=\"${id_name}\" value=\"${id_value}\" data-generated>\n <input type=\"hidden\" name=\"${index_name}\" value=\"${item.index}\" data-generated>`\n );\n }\n\n submit() {\n if (this.inputs.length === 0) return;\n\n this.element.requestSubmit();\n }\n\n clear() {\n this.inputs.forEach((input) => input.remove());\n }\n\n get inputs() {\n return this.element.querySelectorAll(\"input[data-generated]\");\n }\n}\n"],"names":["DragState","constructor","list","event","id","this","cursorOffset","offsetY","initialPosition","target","offsetTop","targetId","updateCursor","row","callback","listOffset","getBoundingClientRect","top","itemPosition","clientY","updateItemPosition","updateScroll","previousScrollOffset","scrollDelta","position","Math","max","min","offsetHeight","Definitions","identifier","controllerConstructor","Controller","static","query","String","sort","queryValueChanged","Turbo","navigator","history","replace","url","sortValueChanged","document","querySelectorAll","sortSelector","forEach","input","value","frame","element","closest","URL","baseURI","window","location","href","search","params","Object","connect","index","Array","from","parentElement","children","indexOf","paramsValueChanged","id_value","dragUpdate","offset","dragOffset","style","zIndex","toggleAttribute","updateVisually","dragIndex","updateIndex","reset","removeAttribute","hasChanges","paramsValue","index_value","round","comparisonIndex","startDragging","dragState","addEventListener","mousemove","mouseup","scroll","stopDragging","removeEventListener","tablesOrderableItemOutlets","item","drop","dragItem","newIndex","targetItem","insertAdjacentElement","commitChanges","tablesOrderableFormOutlet","clear","add","submit","mousedown","isDragging","preventDefault","animate","ticking","requestAnimationFrame","tablesOrderableFormOutlets","form","tablesOrderableFormOutletConnected","tablesOrderableFormOutletDisconnected","currentItems","find","toSorted","a","b","id_name","index_name","insertAdjacentHTML","inputs","length","requestSubmit","remove"],"mappings":"8FA8NA,MAAMA,EAMJ,WAAAC,CAAYC,EAAMC,EAAOC,GAEvBC,KAAKC,aAAeH,EAAMI,QAG1BF,KAAKG,gBAAkBL,EAAMM,OAAOC,UAAYR,EAAKQ,UAGrDL,KAAKM,SAAWP,CACjB,CAUD,YAAAQ,CAAaV,EAAMW,EAAKV,EAAOW,GAG7BT,KAAKU,WAAab,EAAKc,wBAAwBC,IAO/C,IAAIC,EAHmBf,EAAMgB,QAAUd,KAAKU,WAGRV,KAAKC,aAEzCD,MAAKe,EAAoBlB,EAAMW,EAAKK,EAAcJ,EACnD,CAUD,YAAAO,CAAanB,EAAMW,EAAKC,GACtB,MAAMQ,EAAuBjB,KAAKU,WAIlCV,KAAKU,WAAab,EAAKc,wBAAwBC,IAG/C,MAAMM,EAAcD,EAAuBjB,KAAKU,WAG1CS,EAAWnB,KAAKmB,SAAWD,EAEjClB,MAAKe,EAAoBlB,EAAMW,EAAKW,EAAUV,EAC/C,CAED,EAAAM,CAAoBlB,EAAMW,EAAKW,EAAUV,GAEvCU,EAAWC,KAAKC,IAAIF,EAAU,GAC9BA,EAAWC,KAAKE,IAAIH,EAAUtB,EAAK0B,aAAef,EAAIe,cAGtDvB,KAAKmB,SAAWA,EAShBV,EAJeU,EAAWnB,KAAKG,gBAKhC,ECzSE,MAACqB,EAAc,CAClB,CACEC,WAAY,4BACZC,sBCLW,cAAwCC,EACrDC,cAAgB,CACdC,MAAOC,OACPC,KAAMD,QAGR,iBAAAE,CAAkBH,GAChBI,EAAMC,UAAUC,QAAQC,QAAQpC,MAAKqC,EAAKR,GAC3C,CAED,gBAAAS,CAAiBP,GACfQ,SAASC,iBAAiBxC,MAAKyC,GAAeC,SAASC,IACjDA,IAAOA,EAAMC,MAAQb,EAAI,GAEhC,CAED,KAAIU,GACF,MAAO,oBACR,CAED,EAAAJ,CAAKR,GACH,MAAMgB,EAAQ7C,KAAK8C,QAAQC,QAAQ,eACnC,IAAIV,EAUJ,OAPEA,EADEQ,EACI,IAAIG,IAAIH,EAAMI,SAEd,IAAID,IAAIE,OAAOC,SAASC,MAGhCf,EAAIgB,OAASxB,EAENQ,CACR,ID1BD,CACEZ,WAAY,0BACZC,sBEVW,cAAqCC,EAClDC,cAAgB,CACd0B,OAAQC,QAGV,OAAAC,GAoGF,IAAkBV,EAhGd9C,KAAKyD,OAgGSX,EAhGQ9C,KAAKQ,IAiGtBkD,MAAMC,KAAKb,EAAQc,cAAcC,UAAUC,QAAQhB,GAhGzD,CAED,kBAAAiB,CAAmBT,GACjBtD,KAAKD,GAAKuD,EAAOU,QAClB,CAED,UAAAC,CAAWC,GACTlE,KAAKmE,WAAaD,EAClBlE,KAAKQ,IAAI4D,MAAMjD,SAAW,WAC1BnB,KAAKQ,IAAI4D,MAAMxD,IAAMsD,EAAS,KAC9BlE,KAAKQ,IAAI4D,MAAMC,OAAS,IACxBrE,KAAKQ,IAAI8D,gBAAgB,YAAY,EACtC,CAQD,cAAAC,CAAed,GACbzD,KAAKQ,IAAI4D,MAAMjD,SAAW,WAC1BnB,KAAKQ,IAAI4D,MAAMxD,IACbZ,KAAKQ,IAAIe,cAAgBkC,EAAQzD,KAAKwE,WADnB,IAGtB,CASD,WAAAC,CAAYhB,GACVzD,KAAKyD,MAAQA,CACd,CAKD,KAAAiB,UACS1E,KAAKmE,WACZnE,KAAKQ,IAAImE,gBAAgB,SACzB3E,KAAKQ,IAAImE,gBAAgB,WAC1B,CAKD,cAAIC,GACF,OAAO5E,KAAK6E,YAAYC,cAAgB9E,KAAKyD,KAC9C,CASD,aAAIe,GACF,OAAIxE,KAAKmE,YAAkC,IAApBnE,KAAKmE,WACnBnE,KAAKyD,MAAQrC,KAAK2D,MAAM/E,KAAKmE,WAAanE,KAAKQ,IAAIe,cAEnDvB,KAAKyD,KAEf,CAUD,mBAAIuB,GACF,OAAIhF,KAAKmE,WACAnE,KAAKwE,WAAaxE,KAAKmE,WAAa,EAAI,IAAO,IAE/CnE,KAAKyD,KAEf,CAOD,OAAIjD,GACF,OAAOR,KAAK8C,QAAQc,aACrB,IF1FD,CACEnC,WAAY,0BACZC,sBDdW,cAAsCC,EACnDC,eAAiB,CAAC,0BAA2B,2BAI7C,aAAAqD,CAAcC,GACZlF,KAAKkF,UAAYA,EAEjB3C,SAAS4C,iBAAiB,YAAanF,KAAKoF,WAC5C7C,SAAS4C,iBAAiB,UAAWnF,KAAKqF,SAC1CnC,OAAOiC,iBAAiB,SAAUnF,KAAKsF,QAAQ,GAE/CtF,KAAK8C,QAAQsB,MAAMjD,SAAW,UAC/B,CAED,YAAAoE,GACE,MAAML,EAAYlF,KAAKkF,UAUvB,cATOlF,KAAKkF,UAEZ3C,SAASiD,oBAAoB,YAAaxF,KAAKoF,WAC/C7C,SAASiD,oBAAoB,UAAWxF,KAAKqF,SAC7CnC,OAAOsC,oBAAoB,SAAUxF,KAAKsF,QAAQ,GAElDtF,KAAK8C,QAAQ6B,gBAAgB,SAC7B3E,KAAKyF,2BAA2B/C,SAASgD,GAASA,EAAKhB,UAEhDQ,CACR,CAED,IAAAS,GAKE,MAAMC,EAAW5F,KAAK4F,SAEtB,IAAKA,EAAU,OAEf,MAAMC,EAAWD,EAASpB,UACpBsB,EAAa9F,KAAKyF,2BAA2BI,GAE9CC,IAGDD,EAAWD,EAASnC,MACtBqC,EAAWtF,IAAIuF,sBAAsB,cAAeH,EAASpF,KACpDqF,EAAWD,EAASnC,OAC7BqC,EAAWtF,IAAIuF,sBAAsB,WAAYH,EAASpF,KAI5DR,KAAKyF,2BAA2B/C,SAAQ,CAACgD,EAAMjC,IAC7CiC,EAAKjB,YAAYhB,KAInBzD,KAAKgG,gBACN,CAED,aAAAA,GAEEhG,KAAKiG,0BAA0BC,QAG/BlG,KAAKyF,2BAA2B/C,SAASgD,IACnCA,EAAKd,YAAY5E,KAAKiG,0BAA0BE,IAAIT,EAAK,IAG/D1F,KAAKiG,0BAA0BG,QAChC,CAMD,SAAAC,CAAUvG,GACR,GAAIE,KAAKsG,WAAY,OAErB,MAAMlG,EAASJ,MAAK8F,EAAYhG,EAAMM,QAEjCA,IAELN,EAAMyG,iBAENvG,KAAKiF,cAAc,IAAItF,EAAUK,KAAK8C,QAAShD,EAAOM,EAAOL,KAE7DC,KAAKkF,UAAU3E,aAAaP,KAAK8C,QAAS1C,EAAOI,IAAKV,EAAOE,KAAKwG,SACnE,CAEDpB,UAAatF,IACNE,KAAKsG,aAEVxG,EAAMyG,iBAEFvG,KAAKyG,UAETzG,KAAKyG,SAAU,EAEfvD,OAAOwD,uBAAsB,KAC3B1G,KAAKyG,SAAU,EACfzG,KAAKkF,UAAU3E,aACbP,KAAK8C,QACL9C,KAAK4F,SAASpF,IACdV,EACAE,KAAKwG,QACN,KACD,EAGJlB,OAAUxF,IACHE,KAAKsG,aAActG,KAAKyG,UAE7BzG,KAAKyG,SAAU,EAEfvD,OAAOwD,uBAAsB,KAC3B1G,KAAKyG,SAAU,EACfzG,KAAKkF,UAAUlE,aACbhB,KAAK8C,QACL9C,KAAK4F,SAASpF,IACdR,KAAKwG,QACN,IACD,EAGJnB,QAAWvF,IACJE,KAAKsG,aAEVtG,KAAK2F,OACL3F,KAAKuF,eACLvF,KAAK2G,2BAA2BjE,SAASkE,UAAgBA,EAAK1B,YAAU,EAG1E,kCAAA2B,CAAmCD,EAAM9D,GACnC8D,EAAK1B,WAEPlF,KAAKiF,cAAc2B,EAAK1B,UAE3B,CAED,qCAAA4B,CAAsCF,EAAM9D,GACtC9C,KAAKsG,aAEPM,EAAK1B,UAAYlF,KAAKuF,eAEzB,CAaDiB,QAAWtC,IACT,MAAM0B,EAAW5F,KAAK4F,SAGtBA,EAAS3B,WAAWC,GAIpBlE,MAAK+G,EAAcrE,SAAQ,CAACgD,EAAMjC,KAC5BiC,IAASE,GACbF,EAAKnB,eAAed,EAAM,GAC1B,EAGJ,cAAI6C,GACF,QAAStG,KAAKkF,SACf,CAED,YAAIU,GACF,OAAK5F,KAAKsG,WAEHtG,KAAKyF,2BAA2BuB,MACpCtB,GAASA,EAAK3F,KAAOC,KAAKkF,UAAU5E,WAHV,IAK9B,CAQD,KAAIyG,GACF,OAAO/G,KAAKyF,2BAA2BwB,UACrC,CAACC,EAAGC,IAAMD,EAAElC,gBAAkBmC,EAAEnC,iBAEnC,CAQD,EAAAc,CAAYhD,GACV,OAAO9C,KAAKyF,2BAA2BuB,MACpCtB,GAASA,EAAK5C,UAAYA,GAE9B,IC7LD,CACErB,WAAY,0BACZC,sBGlBW,cAAsCC,EACnD,GAAAwE,CAAIT,GACF,MAAM0B,QAAEA,EAAOpD,SAAEA,EAAQqD,WAAEA,GAAe3B,EAAKb,YAC/C7E,KAAK8C,QAAQwE,mBACX,YACA,8BAA8BF,aAAmBpD,gEACZqD,aAAsB3B,EAAKjC,yBAEnE,CAED,MAAA2C,GAC6B,IAAvBpG,KAAKuH,OAAOC,QAEhBxH,KAAK8C,QAAQ2E,eACd,CAED,KAAAvB,GACElG,KAAKuH,OAAO7E,SAASC,GAAUA,EAAM+E,UACtC,CAED,UAAIH,GACF,OAAOvH,KAAK8C,QAAQN,iBAAiB,wBACtC"}
@@ -1 +1 @@
1
- //= link_tree ../javascripts
1
+ //= link_tree ../builds
@@ -26,9 +26,9 @@ module Katalyst
26
26
  def default_html_attributes
27
27
  {
28
28
  data: {
29
- controller: "tables--turbo-collection",
30
- tables__turbo_collection_query_value: current_query,
31
- tables__turbo_collection_sort_value: collection.sort,
29
+ controller: "tables--turbo--collection",
30
+ tables__turbo__collection_query_value: current_query,
31
+ tables__turbo__collection_sort_value: collection.sort,
32
32
  },
33
33
  }
34
34
  end
@@ -0,0 +1,25 @@
1
+ import TurboCollectionController from "./turbo/collection_controller";
2
+ import ItemController from "./orderable/item_controller";
3
+ import ListController from "./orderable/list_controller";
4
+ import FormController from "./orderable/form_controller";
5
+
6
+ const Definitions = [
7
+ {
8
+ identifier: "tables--turbo--collection",
9
+ controllerConstructor: TurboCollectionController,
10
+ },
11
+ {
12
+ identifier: "tables--orderable--item",
13
+ controllerConstructor: ItemController,
14
+ },
15
+ {
16
+ identifier: "tables--orderable--list",
17
+ controllerConstructor: ListController,
18
+ },
19
+ {
20
+ identifier: "tables--orderable--form",
21
+ controllerConstructor: FormController,
22
+ },
23
+ ];
24
+
25
+ export { Definitions as default };
@@ -6,7 +6,7 @@ export default class OrderableFormController extends Controller {
6
6
  this.element.insertAdjacentHTML(
7
7
  "beforeend",
8
8
  `<input type="hidden" name="${id_name}" value="${id_value}" data-generated>
9
- <input type="hidden" name="${index_name}" value="${item.index}" data-generated>`,
9
+ <input type="hidden" name="${index_name}" value="${item.index}" data-generated>`
10
10
  );
11
11
  }
12
12
 
@@ -52,7 +52,7 @@ export default class OrderableListController extends Controller {
52
52
 
53
53
  // reindex all items based on their new positions
54
54
  this.tablesOrderableItemOutlets.forEach((item, index) =>
55
- item.updateIndex(index),
55
+ item.updateIndex(index)
56
56
  );
57
57
 
58
58
  // save the changes
@@ -104,7 +104,7 @@ export default class OrderableListController extends Controller {
104
104
  this.element,
105
105
  this.dragItem.row,
106
106
  event,
107
- this.animate,
107
+ this.animate
108
108
  );
109
109
  });
110
110
  };
@@ -119,7 +119,7 @@ export default class OrderableListController extends Controller {
119
119
  this.dragState.updateScroll(
120
120
  this.element,
121
121
  this.dragItem.row,
122
- this.animate,
122
+ this.animate
123
123
  );
124
124
  });
125
125
  };
@@ -179,7 +179,7 @@ export default class OrderableListController extends Controller {
179
179
  if (!this.isDragging) return null;
180
180
 
181
181
  return this.tablesOrderableItemOutlets.find(
182
- (item) => item.id === this.dragState.targetId,
182
+ (item) => item.id === this.dragState.targetId
183
183
  );
184
184
  }
185
185
 
@@ -191,7 +191,7 @@ export default class OrderableListController extends Controller {
191
191
  */
192
192
  get #currentItems() {
193
193
  return this.tablesOrderableItemOutlets.toSorted(
194
- (a, b) => a.comparisonIndex - b.comparisonIndex,
194
+ (a, b) => a.comparisonIndex - b.comparisonIndex
195
195
  );
196
196
  }
197
197
 
@@ -203,7 +203,7 @@ export default class OrderableListController extends Controller {
203
203
  */
204
204
  #targetItem(element) {
205
205
  return this.tablesOrderableItemOutlets.find(
206
- (item) => item.element === element,
206
+ (item) => item.element === element
207
207
  );
208
208
  }
209
209
 
data/config/importmap.rb CHANGED
@@ -1,5 +1,2 @@
1
1
  # frozen_string_literal: true
2
-
3
- pin_all_from Katalyst::Tables::Engine.root.join("app/assets/javascripts"),
4
- # preload in tests so that we don't start clicking before controllers load
5
- preload: Rails.env.test?
2
+ pin "@katalyst/tables", to: "katalyst/tables.js"
@@ -19,7 +19,7 @@ module Katalyst
19
19
  #
20
20
  # @return [[SortForm, ActiveRecord::Relation]]
21
21
  def table_sort(collection)
22
- column, direction = params[:sort]&.split(" ")
22
+ column, direction = params[:sort]&.split
23
23
  direction = "asc" unless SortForm::DIRECTIONS.include?(direction)
24
24
 
25
25
  SortForm.new(column: column,
@@ -9,14 +9,16 @@ module Katalyst
9
9
 
10
10
  initializer "katalyst-tables.asset" do
11
11
  config.after_initialize do |app|
12
- app.config.assets.precompile += %w[katalyst-tables.js] if app.config.respond_to?(:assets)
12
+ if app.config.respond_to?(:assets)
13
+ app.config.assets.precompile += %w[katalyst-tables.js]
14
+ end
13
15
  end
14
16
  end
15
17
 
16
18
  initializer "katalyst-tables.importmap", before: "importmap" do |app|
17
19
  if app.config.respond_to?(:importmap)
18
20
  app.config.importmap.paths << root.join("config/importmap.rb")
19
- app.config.importmap.cache_sweepers << root.join("app/assets/javascripts")
21
+ app.config.importmap.cache_sweepers << root.join("app/assets/builds")
20
22
  end
21
23
  end
22
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-tables
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.12
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-05 00:00:00.000000000 Z
11
+ date: 2023-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: katalyst-html-attributes
@@ -49,11 +49,11 @@ files:
49
49
  - CHANGELOG.md
50
50
  - LICENSE.txt
51
51
  - README.md
52
+ - app/assets/builds/katalyst/tables.esm.js
53
+ - app/assets/builds/katalyst/tables.js
54
+ - app/assets/builds/katalyst/tables.min.js
55
+ - app/assets/builds/katalyst/tables.min.js.map
52
56
  - app/assets/config/katalyst-tables.js
53
- - app/assets/javascripts/controllers/tables/orderable/form_controller.js
54
- - app/assets/javascripts/controllers/tables/orderable/item_controller.js
55
- - app/assets/javascripts/controllers/tables/orderable/list_controller.js
56
- - app/assets/javascripts/controllers/tables/turbo_collection_controller.js
57
57
  - app/components/concerns/katalyst/tables/configurable_component.rb
58
58
  - app/components/concerns/katalyst/tables/has_table_content.rb
59
59
  - app/components/concerns/katalyst/tables/orderable.rb
@@ -70,6 +70,11 @@ files:
70
70
  - app/components/katalyst/tables/pagy_nav_component.rb
71
71
  - app/components/katalyst/turbo/pagy_nav_component.rb
72
72
  - app/components/katalyst/turbo/table_component.rb
73
+ - app/javascript/tables/application.js
74
+ - app/javascript/tables/orderable/form_controller.js
75
+ - app/javascript/tables/orderable/item_controller.js
76
+ - app/javascript/tables/orderable/list_controller.js
77
+ - app/javascript/tables/turbo/collection_controller.js
73
78
  - app/models/concerns/katalyst/tables/collection/core.rb
74
79
  - app/models/concerns/katalyst/tables/collection/pagination.rb
75
80
  - app/models/concerns/katalyst/tables/collection/reducers.rb
@@ -100,14 +105,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
105
  requirements:
101
106
  - - ">="
102
107
  - !ruby/object:Gem::Version
103
- version: 2.6.0
108
+ version: 3.0.0
104
109
  required_rubygems_version: !ruby/object:Gem::Requirement
105
110
  requirements:
106
111
  - - ">="
107
112
  - !ruby/object:Gem::Version
108
113
  version: '0'
109
114
  requirements: []
110
- rubygems_version: 3.4.20
115
+ rubygems_version: 3.4.10
111
116
  signing_key:
112
117
  specification_version: 4
113
118
  summary: HTML table generator for Rails views