katalyst-tables 2.2.12 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea4e17cca2f486696e4cc5b8bff2bc6b06aeb33e131f0774f881b324964eae90
4
- data.tar.gz: 5bc402aef25bee8f49418225d603f36246565622749f1fa6d06d922f341f6348
3
+ metadata.gz: f02c8db5c8d61ac1a1e678ac9e4c020826c2289dc25a584b82a7786e859d9023
4
+ data.tar.gz: 9d32b27a803509807facf9981f8b53eb8e696f5daa6e816427e32bc5d7c1bd03
5
5
  SHA512:
6
- metadata.gz: d87f264fb30eab8e5a9f43b1a85464f5b0fc32f499e11bc64acc745a804dda081ed58e3eb368b1a50b4e01eeb28b6ace4e9b550a86fb0addb2cf4fb0a61aa38d
7
- data.tar.gz: 3afaa276a44921bdb7a5b62d60701d77c71da24741a651b0fe3b89c36a2fc059b037fe1c5859680f6be43c0bb324fe4e9924067b2d0c2b58cffcad1c1b7b585a
6
+ metadata.gz: e1c10b35c516f19094d4447db4fdb597847d650dce403a7f1062e04a3d35b17a2fb706e6e10f253b14300999e3570057bbc287d414c163c5e4440b5ac389349c
7
+ data.tar.gz: c5ff9bbe6c0f0e159e8254eb97f92805528fc7a5d2f4b53f616d0a1a13211d512d421f10843568026b6b6788d8c5753a96444fd7d5ec7785efb508b20df03d56
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--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--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,r){this.listOffset=t.getBoundingClientRect().top;let i=s.clientY-this.listOffset-this.cursorOffset;this.#t(t,e,i,r)}updateScroll(t,e,s){const r=this.listOffset;this.listOffset=t.getBoundingClientRect().top;const i=r-this.listOffset,a=this.position+i;this.#t(t,e,a,s)}#t(t,e,s,r){s=Math.max(s,0),s=Math.min(s,t.offsetHeight-e.offsetHeight),this.position=s;r(s-this.initialPosition)}}const r=[{identifier:"tables--turbo--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.#r(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.#i.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#i(){return this.tablesOrderableItemOutlets.toSorted(((t,e)=>t.comparisonIndex-e.comparisonIndex))}#r(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:r}=t.paramsValue;this.element.insertAdjacentHTML("beforeend",`<input type="hidden" name="${e}" value="${s}" data-generated>\n <input type="hidden" name="${r}" 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{r 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/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/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--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":"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"}
@@ -1 +1 @@
1
- //= link_tree ../javascripts
1
+ //= link_tree ../builds
@@ -0,0 +1,25 @@
1
+ import TurboCollectionController from "./turbo/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--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.0
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/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