bullet_train 1.29.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27989f1d995ce633ff168f07ac58624a8f5149415455a08806fcbff828cf7f51
4
- data.tar.gz: a3ba128f84110e3343f003d4afe11f1ca86cb20deb858ac0d5fdbff4ed3b7859
3
+ metadata.gz: 2b557b6927fba1606682c07f0fe0cb44e37e02a1051974ed6bf88ef9bd3234e6
4
+ data.tar.gz: 5ee2949f57d715683f3a3e5091c042fdc4be4c6e51669b3301c5654a072bb48d
5
5
  SHA512:
6
- metadata.gz: 430d8035188fc0a3914ecb83e0ca763d3f4aa11c40d948b0664f522aae694fbdb8f7bf2a0994cd9a246831fb0973ac51ee0cf13fdd236e6727582c8457581a55
7
- data.tar.gz: f1b116ce3f0b765552e404cdbe1c99a714aada1f01b2a09670febe37b574c3554518495ec35172871ead9f1f20b8094cee9e11c684b9d649d646cc456a7adb41
6
+ metadata.gz: d3cc899b2d9b984608847279d380891c948a635ec92829b931369cc6ce7e1a3ca4e75df95e49c629a1fa17a6d163b95becd351d88a7e23d7e406aa45100cb2ca
7
+ data.tar.gz: e11f8580c554efe009775ba1bc18ac77bf03e58a5a69c8a667de6c24bb31c1f941c65f64e5ffd0dd1daca3c34981c8f6aebf6bfd94a3c590dd35d1990eebac92
@@ -0,0 +1,188 @@
1
+ # Super Scaffolding with the `--sortable` option
2
+
3
+ <div class="rounded-md border bg-amber-100 border-amber-200 py-4 px-5 mb-3 not-prose">
4
+ <h3 class="text-sm text-amber-800 font-light mb-2">
5
+ Note: These docs are for the old sortable controller based on `dragula`.
6
+ </h3>
7
+ <p class="text-sm text-amber-800 font-light">
8
+ <a href="/docs/super-scaffolding/sortable">You can find the new documentation here.</a>
9
+ </p>
10
+ </div>
11
+
12
+ ## Continuing to use the `dragula` based sortable controller
13
+
14
+ We no longer include the old `dragula` controller in the NPM package for `bullet_train-sortable` because doing so would require `dragula` to still be a hard dependency.
15
+
16
+ If you want to continue using that controller you'll need to do a few things.
17
+
18
+ ### 1. Add `dragula` and `jquery` as dependencies in your package.json
19
+
20
+ Since we don't include `dragula` and `jquery` as dependencies anymore you need to include them in your own `package.json`.
21
+
22
+ ```
23
+ yarn add dragula jquery
24
+ ```
25
+
26
+ ### 2. Copy `dragula-sortable_controller.js` into your project
27
+
28
+ You can grab the old controller [from the `core` repo here](https://github.com/bullet-train-co/bullet_train-core/blob/main/bullet_train-sortable/app/javascript/controllers/dragula-sortable_controller.js).
29
+
30
+ You should put it in your app at `app/javascript/controllers/dragule-sortable_controller.js`
31
+
32
+ ### 3. Update references to the sortable controller to use `dragula-sortable`
33
+
34
+ Assuming you have a sortable `Page` model the file you need to update is `app/views/account/pages/_index.html.erb`.
35
+
36
+ On the `<tbody>` you need to change `data-controller="sortable"` to be `data-controller="dragula-sortable"`.
37
+
38
+ If you're responding to any of the events emitted by the controller they will also need to be changed from `sortable` to `dragula-sortable`.
39
+
40
+ ## Old docs
41
+
42
+ The remainder of this page is the original documentation for the dragula based sortable controller. It has been updated with the assumption that your dragula based Stimulus controller will be in the file `dragula-sortable_controller.js`.
43
+
44
+ ---
45
+
46
+ When issuing a `rails generate super_scaffold` command, you can pass the `--sortable` option like this:
47
+
48
+ ```
49
+ # E.g. Pages belong to a Site and are sortable via drag-and-drop:
50
+ rails generate super_scaffold Page Site,Team name:text_field path:text_area --sortable
51
+ ```
52
+
53
+ The `--sortable` option:
54
+
55
+ 1. Wraps the table's body in a `sortable` Stimulus controller, providing drag-and-drop re-ordering;
56
+ 2. Adds a `reorder` action to your resource via `include SortableActions`, triggered automatically on re-order;
57
+ 3. Adds a `sort_order` attribute to your model to store the ordering;
58
+ 4. Adds a `default_scope` which orders by `sort_order` and auto increments `sort_order` on create via `include Sortable` on the model.
59
+
60
+ ## Disabling Saving on Re-order
61
+
62
+ By default, a call to save the new `sort_order` is triggered automatically on re-order.
63
+
64
+ ### To disable auto-saving
65
+
66
+ Add the `data-dragula-sortable-save-on-reorder-value="false"` param on the `dragula-sortable` root element:
67
+
68
+ ```html
69
+ <tbody data-controller="dragula-sortable"
70
+ data-dragula-sortable-save-on-reorder-value="false"
71
+ ...
72
+ >
73
+ ```
74
+
75
+ ### To manually fire the save action via a button
76
+
77
+ Since the button won't be part of the `dragula-sortable` root element's descendants (all its direct descendants are sortable by default), you'll need to wrap both the `dragula-sortable` element and the save button in a new Stimulus controlled ancestor element. On the button, add a `data-action`.
78
+
79
+ For instance:
80
+
81
+ ```html
82
+ <div data-controller="dragula-sortable-wrapper">
83
+ <table>...</table>
84
+ <button data-action="dragula-sortable-wrapper#saveSortOrder">Save Sort Order</button>
85
+ </div>
86
+ ```
87
+
88
+ ```js
89
+ /* dragula-sortable-wrapper_controller.js */
90
+ import { Controller } from "@hotwired/stimulus"
91
+
92
+ export default class extends Controller {
93
+ static targets = [ "dragula-sortable" ]
94
+
95
+ saveSortOrder() {
96
+ if (!this.hasSortableTarget) { return }
97
+ this.sortableTarget.dispatchEvent(new CustomEvent("save-sort-order"))
98
+ }
99
+ }
100
+ ```
101
+
102
+ And on the `dragula-sortable` element, catch the `save-sort-order` event and define it as the `dragula-sortable` target for the `dragula-sortable-wrapper` controller:
103
+
104
+ ```html
105
+ <tbody data-controller="dragula-sortable"
106
+ data-dragula-sortable-save-on-reorder-value="false"
107
+ data-action="save-sort-order->dragula-sortable#saveSortOrder"
108
+ data-dragula-sortable-wrapper-target="dragula-sortable"
109
+ ...
110
+ >
111
+ ```
112
+
113
+ ## Events
114
+
115
+ Under the hood, the `dragula-sortable` Stimulus controller uses the [dragula](https://github.com/bevacqua/dragula) library.
116
+
117
+ All of the events that `dragula` defines are re-dispatched as native DOM events. The native DOM event name is prefixed with `dragula-sortable:`
118
+
119
+ | dragula event name | DOM event name |
120
+ |---------------------|------------------------------|
121
+ | drag | dragula-sortable:drag |
122
+ | dragend | dragula-sortable:dragend |
123
+ | drop | dragula-sortable:drop |
124
+ | cancel | dragula-sortable:cancel |
125
+ | remove | dragula-sortable:remove |
126
+ | shadow | dragula-sortable:shadow |
127
+ | over | dragula-sortable:over |
128
+ | out | dragula-sortable:out |
129
+ | cloned | dragula-sortable:cloned |
130
+
131
+ The original event's listener arguments are passed to the native DOM event as a simple numbered Array under `event.detail.args`. See [dragula's list of events](https://github.com/bevacqua/dragula#drakeon-events) for the listener arguments.
132
+
133
+ ### Example: Asking for Confirmation on the `drop` Event
134
+
135
+ Let's say we'd like to ask the user to confirm before saving the new sort order:
136
+
137
+ > Are you sure you want to place DROPPED ITEM before SIBLING ITEM?
138
+
139
+ Add a `data-controller` attribute to the `<table>` tag that wraps the sortable `<tbody>`:
140
+
141
+ ```html
142
+ <table data-controller="confirm-reorder">
143
+ ```
144
+
145
+ ```js
146
+ /* confirm-reorder_controller.js */
147
+ import { Controller } from "@hotwired/stimulus"
148
+
149
+ export default class extends Controller {
150
+ static targets = [ "dragula-sortable" ]
151
+
152
+ requestConfirmation(event) {
153
+ const [el, target, source, sibling] = event.detail?.args
154
+
155
+ // sibling will be undefined if dropped in last position, taking a shortcut here
156
+ const areYouSure = `Are you sure you want to place ${el.dataset.name} before ${sibling.dataset.name}?`
157
+
158
+ // let's suppose each <tr> in sortable has a data-name attribute
159
+ if (confirm(areYouSure)) {
160
+ this.sortableTarget.dispatchEvent(new CustomEvent('save-sort-order'))
161
+ } else {
162
+ this.revertToOriginalOrder()
163
+ }
164
+ }
165
+
166
+ prepareForRevertOnCancel(event) {
167
+ // we're assuming we can swap out the HTML safely
168
+ this.originalSortableHTML = this.sortableTarget.innerHTML
169
+ }
170
+
171
+ revertToOriginalOrder() {
172
+ if (this.originalSortableHTML === undefined) { return }
173
+ this.sortableTarget.innerHTML = this.originalSortableHTML
174
+ this.originalSortableHTML = undefined
175
+ }
176
+ }
177
+ ```
178
+
179
+ And on the `dragula-sortable` element, catch the `dragula-sortable:drop`, `dragula-sortable:drag` (for catching when dragging starts) and `save-sort-order` events. Also define it as the `dragula-sortable` target for the `confirm-reorder` controller:
180
+
181
+ ```html
182
+ <tbody data-controller="dragula-sortable"
183
+ data-dragula-sortable-save-on-reorder-value="false"
184
+ data-action="dragula-sortable:drop->confirm-reorder#requestConfirmation dragula-sortable:drag->confirm-reorder#prepareForRevertOnCancel save-sort-order->dragula-sortable#saveSortOrder"
185
+ data-confirm-reorder-target="dragula-sortable"
186
+ ...
187
+ >
188
+ ```
@@ -1,5 +1,17 @@
1
1
  # Super Scaffolding with the `--sortable` option
2
2
 
3
+ <div class="rounded-md border bg-amber-100 border-amber-200 py-4 px-5 mb-3 not-prose">
4
+ <h3 class="text-sm text-amber-800 font-light mb-2">
5
+ Note: The sortable controller and these docs have recently changed.
6
+ </h3>
7
+ <p class="text-sm text-amber-800 font-light mb-2">
8
+ These instructions are for the new `sortable_controller.js` which has removed the dependency on `dragula` and which works slightly differently.
9
+ </p>
10
+ <p class="text-sm text-amber-800 font-light">
11
+ <a href="/docs/super-scaffolding/dragula-sortable">You can find the old documentation for the `dragula` based controller here.</a>
12
+ </p>
13
+ </div>
14
+
3
15
  When issuing a `rails generate super_scaffold` command, you can pass the `--sortable` option like this:
4
16
 
5
17
  ```
@@ -31,7 +43,16 @@ Add the `data-sortable-save-on-reorder-value="false"` param on the `sortable` r
31
43
 
32
44
  ### To manually fire the save action via a button
33
45
 
34
- Since the button won't be part of the `sortable` root element's descendants (all its direct descendants are sortable by default), you'll need to wrap both the `sortable` element and the save button in a new Stimulus controlled ancestor element.
46
+ Since the button won't be part of the `sortable` root element's descendants (all its direct descendants are sortable by default), you'll need to wrap both the `sortable` element and the save button in a new Stimulus controlled ancestor element. On the button, add a `data-action`.
47
+
48
+ For instance:
49
+
50
+ ```html
51
+ <div data-controller="sortable-wrapper">
52
+ <table>...</table>
53
+ <button data-action="sortable-wrapper#saveSortOrder">Save Sort Order</button>
54
+ </div>
55
+ ```
35
56
 
36
57
  ```js
37
58
  /* sortable-wrapper_controller.js */
@@ -39,7 +60,7 @@ import { Controller } from "@hotwired/stimulus"
39
60
 
40
61
  export default class extends Controller {
41
62
  static targets = [ "sortable" ]
42
-
63
+
43
64
  saveSortOrder() {
44
65
  if (!this.hasSortableTarget) { return }
45
66
  this.sortableTarget.dispatchEvent(new CustomEvent("save-sort-order"))
@@ -47,12 +68,6 @@ export default class extends Controller {
47
68
  }
48
69
  ```
49
70
 
50
- On the button, add a `data-action`
51
-
52
- ```html
53
- <button data-action="sortable-wrapper#saveSortOrder">Save Sort Order</button>
54
- ```
55
-
56
71
  And on the `sortable` element, catch the `save-sort-order` event and define it as the `sortable` target for the `sortable-wrapper` controller:
57
72
 
58
73
  ```html
@@ -66,23 +81,35 @@ And on the `sortable` element, catch the `save-sort-order` event and define it a
66
81
 
67
82
  ## Events
68
83
 
69
- Under the hood, the `sortable` Stimulus controller uses the [dragula](https://github.com/bevacqua/dragula) library.
84
+ Under the hood, the `sortable` Stimulus controller uses native drag and drop event handling as provided by modern browsers.
70
85
 
71
- All of the events that `dragula` defines are re-dispatched as native DOM events. The native DOM event name is prefixed with `sortable:`
86
+ The following events are dispatched when significant events related to the list occur. (Note these are new events that were not previously emitted by the `dragula` based controller.)
72
87
 
73
- | dragula event name | DOM event name |
74
- |---------------------|----------------------|
75
- | drag | sortable:drag |
76
- | dragend | sortable:dragend |
77
- | drop | sortable:drop |
78
- | cancel | sortable:cancel |
79
- | remove | sortable:remove |
80
- | shadow | sortable:shadow |
81
- | over | sortable:over |
82
- | out | sortable:out |
83
- | cloned | sortable:cloned |
88
+ | Event name | Fired when |
89
+ |--------------------|-----------------------------------------------------|
90
+ | sortable:start | a drag via drag handle is started |
91
+ | sortable:reordered | the items in the list are reorderd during a drag |
92
+ | sortable:end | a sortable item is released |
93
+ | sortable:saved | the new order has been persisted after a drop |
84
94
 
85
- The original event's listener arguments are passed to the native DOM event as a simple numbered Array under `event.detail.args`. See [dragula's list of events](https://github.com/bevacqua/dragula#drakeon-events) for the listener arguments.
95
+ These are events that we'll emit to retain some backwards compatibility with the old `dragula` based controller. Be aware that they may not behave exactly the same. In general you should prefer the events above.
96
+
97
+ | Event name | Fired when | Better option |
98
+ |------------------|----------------------------------------------------|--------------------|
99
+ | sortable:drag | a drag via drag handle is started | sortable:start |
100
+ | sortable:dragend | a sortable item is released | sortable:end |
101
+ | sortable:drop | a sortable item is released | sortable:end |
102
+ | sortable:shadow | the items in the list are reorderd during a drag | sortable:reordered |
103
+
104
+ **Note**: The old `dragula` based controller used to emit a few events that were particular to `dragula`. The new controller **does not** emit the following events:
105
+
106
+ | event name |
107
+ |----------------------|
108
+ | sortable:cancel |
109
+ | sortable:remove |
110
+ | sortable:over |
111
+ | sortable:out |
112
+ | sortable:cloned |
86
113
 
87
114
  ### Example: Asking for Confirmation on the `drop` Event
88
115
 
@@ -90,19 +117,25 @@ Let's say we'd like to ask the user to confirm before saving the new sort order:
90
117
 
91
118
  > Are you sure you want to place DROPPED ITEM before SIBLING ITEM?
92
119
 
120
+ Add a `data-controller` attribute to the `<table>` tag that wraps the sortable `<tbody>`:
121
+
122
+ ```html
123
+ <table data-controller="confirm-reorder">
124
+ ```
125
+
93
126
  ```js
94
127
  /* confirm-reorder_controller.js */
95
128
  import { Controller } from "@hotwired/stimulus"
96
129
 
97
130
  export default class extends Controller {
98
131
  static targets = [ "sortable" ]
99
-
132
+
100
133
  requestConfirmation(event) {
101
134
  const [el, target, source, sibling] = event.detail?.args
102
-
135
+
103
136
  // sibling will be undefined if dropped in last position, taking a shortcut here
104
137
  const areYouSure = `Are you sure you want to place ${el.dataset.name} before ${sibling.dataset.name}?`
105
-
138
+
106
139
  // let's suppose each <tr> in sortable has a data-name attribute
107
140
  if (confirm(areYouSure)) {
108
141
  this.sortableTarget.dispatchEvent(new CustomEvent('save-sort-order'))
@@ -110,12 +143,12 @@ export default class extends Controller {
110
143
  this.revertToOriginalOrder()
111
144
  }
112
145
  }
113
-
146
+
114
147
  prepareForRevertOnCancel(event) {
115
148
  // we're assuming we can swap out the HTML safely
116
149
  this.originalSortableHTML = this.sortableTarget.innerHTML
117
150
  }
118
-
151
+
119
152
  revertToOriginalOrder() {
120
153
  if (this.originalSortableHTML === undefined) { return }
121
154
  this.sortableTarget.innerHTML = this.originalSortableHTML
@@ -124,13 +157,49 @@ export default class extends Controller {
124
157
  }
125
158
  ```
126
159
 
127
- And on the `sortable` element, catch the `sortable:drop`, `sortable:drag` (for catching when dragging starts) and `save-sort-order` events. Also define it as the `sortable` target for the `confirm-reorder` controller:
160
+ And on the `sortable` element, catch the `sortable:end`, `sortable:start` (for catching when dragging starts) and `save-sort-order` events. Also define it as the `sortable` target for the `confirm-reorder` controller:
128
161
 
129
162
  ```html
130
163
  <tbody data-controller="sortable"
131
164
  data-sortable-save-on-reorder-value="false"
132
- data-action="sortable:drop->confirm-reorder#requestConfirmation sortable:drag->confirm-reorder#prepareForRevertOnCancel save-sort-order->sortable#saveSortOrder"
165
+ data-action="sortable:end->confirm-reorder#requestConfirmation sortable:start->confirm-reorder#prepareForRevertOnCancel save-sort-order->sortable#saveSortOrder"
133
166
  data-confirm-reorder-target="sortable"
134
167
  ...
135
168
  >
136
169
  ```
170
+
171
+ ## Drag handles
172
+
173
+ With the release of the new `sortable_controller` new super scaffolds will include a cell at the begining of each row with an icon as a drag handle.
174
+
175
+ For pre-existing super scaffolds the new controller will detect that the handles are missing and it will programatically add them to the table.
176
+
177
+ If you want more control over the handles you should update your templates to include handles, which will prevent them from being automatically added.
178
+
179
+ Using the example of a sortable `Page` model from above you'll want to update two files.
180
+
181
+ ### `app/views/account/pages/_page.html.erb`
182
+
183
+ In `app/views/account/pages/_page.html.erb` you should add a `<td>` as the first cell in the `<tr>`. Be sure to include `data-sortable-target="handle"` on the cell so that the controller recognizes it as a handle.
184
+
185
+ ```html
186
+ <tr data-id="<%= page.id %>">
187
+ <td class="cursor-grab active:cursor-grabbing" data-sortable-target="handle"><i class="ti ti-menu opacity-25 group-hover:opacity-100"></i></td> <!-- Add this line! -->
188
+ <!-- Your existing cells here -->
189
+ </tr>
190
+ ```
191
+
192
+ ### `app/views/account/pages/_index.html.erb`
193
+
194
+ In `app/views/account/pages/_index.html.erb` you should add an empty `<td>` as the first cell within the `<tr>` in the `<thead>`. You probably want to set a width for that cell to make things look nice.
195
+
196
+ ```html
197
+ <thead>
198
+ <tr>
199
+ <th class="w-6"></th> <!-- Add this line! -->
200
+ <!-- Your existing cells here -->
201
+ </tr>
202
+ </thead>
203
+ ```
204
+
205
+
@@ -1,3 +1,3 @@
1
1
  module BulletTrain
2
- VERSION = "1.29.0"
2
+ VERSION = "1.30.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.29.0
4
+ version: 1.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
@@ -624,6 +624,7 @@ files:
624
624
  - docs/stylesheets.md
625
625
  - docs/super-scaffolding.md
626
626
  - docs/super-scaffolding/delegated-types.md
627
+ - docs/super-scaffolding/dragula-sortable.md
627
628
  - docs/super-scaffolding/options.md
628
629
  - docs/super-scaffolding/sortable.md
629
630
  - docs/super-scaffolding/targets.md