--sortable
option
Super Scaffolding with the Note: These docs are for the old sortable controller based on `dragula`.
dragula
based sortable controller
Continuing to use the 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.
If you want to continue using that controller you'll need to do a few things.
dragula
and jquery
as dependencies in your package.json
1. Add Since we don't include dragula
and jquery
as dependencies anymore you need to include them in your own package.json
.
yarn add dragula jquery
dragula-sortable_controller.js
into your project
2. Copy You can grab the old controller from the core
repo here.
You should put it in your app at app/javascript/controllers/dragule-sortable_controller.js
dragula-sortable
3. Update references to the sortable controller to use Assuming you have a sortable Page
model the file you need to update is app/views/account/pages/_index.html.erb
.
On the <tbody>
you need to change data-controller="sortable"
to be data-controller="dragula-sortable"
.
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
.
Old docs
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
.
When issuing a rails generate super_scaffold
command, you can pass the --sortable
option like this:
# E.g. Pages belong to a Site and are sortable via drag-and-drop:
rails generate super_scaffold Page Site,Team name:text_field path:text_area --sortable
The --sortable
option:
- Wraps the table's body in a
sortable
Stimulus controller, providing drag-and-drop re-ordering; - Adds a
reorder
action to your resource viainclude SortableActions
, triggered automatically on re-order; - Adds a
sort_order
attribute to your model to store the ordering; - Adds a
default_scope
which orders bysort_order
and auto incrementssort_order
on create viainclude Sortable
on the model.
Disabling Saving on Re-order
By default, a call to save the new sort_order
is triggered automatically on re-order.
To disable auto-saving
Add the data-dragula-sortable-save-on-reorder-value="false"
param on the dragula-sortable
root element:
<tbody data-controller="dragula-sortable"
data-dragula-sortable-save-on-reorder-value="false"
...
>
To manually fire the save action via a button
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
.
For instance:
<div data-controller="dragula-sortable-wrapper">
<table>...</table>
<button data-action="dragula-sortable-wrapper#saveSortOrder">Save Sort Order</button>
</div>
/* dragula-sortable-wrapper_controller.js */
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "dragula-sortable" ]
saveSortOrder() {
if (!this.hasSortableTarget) { return }
this.sortableTarget.dispatchEvent(new CustomEvent("save-sort-order"))
}
}
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:
<tbody data-controller="dragula-sortable"
data-dragula-sortable-save-on-reorder-value="false"
data-action="save-sort-order->dragula-sortable#saveSortOrder"
data-dragula-sortable-wrapper-target="dragula-sortable"
...
>
Events
Under the hood, the dragula-sortable
Stimulus controller uses the dragula library.
All of the events that dragula
defines are re-dispatched as native DOM events. The native DOM event name is prefixed with dragula-sortable:
dragula event name | DOM event name |
---|---|
drag | dragula-sortable:drag |
dragend | dragula-sortable:dragend |
drop | dragula-sortable:drop |
cancel | dragula-sortable:cancel |
remove | dragula-sortable:remove |
shadow | dragula-sortable:shadow |
over | dragula-sortable:over |
out | dragula-sortable:out |
cloned | dragula-sortable:cloned |
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 for the listener arguments.
drop
Event
Example: Asking for Confirmation on the Let's say we'd like to ask the user to confirm before saving the new sort order:
Are you sure you want to place DROPPED ITEM before SIBLING ITEM?
Add a data-controller
attribute to the <table>
tag that wraps the sortable <tbody>
:
<table data-controller="confirm-reorder">
/* confirm-reorder_controller.js */
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "dragula-sortable" ]
requestConfirmation(event) {
const [el, target, source, sibling] = event.detail?.args
// sibling will be undefined if dropped in last position, taking a shortcut here
const areYouSure = `Are you sure you want to place ${el.dataset.name} before ${sibling.dataset.name}?`
// let's suppose each <tr> in sortable has a data-name attribute
if (confirm(areYouSure)) {
this.sortableTarget.dispatchEvent(new CustomEvent('save-sort-order'))
} else {
this.revertToOriginalOrder()
}
}
prepareForRevertOnCancel(event) {
// we're assuming we can swap out the HTML safely
this.originalSortableHTML = this.sortableTarget.innerHTML
}
revertToOriginalOrder() {
if (this.originalSortableHTML === undefined) { return }
this.sortableTarget.innerHTML = this.originalSortableHTML
this.originalSortableHTML = undefined
}
}
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:
<tbody data-controller="dragula-sortable"
data-dragula-sortable-save-on-reorder-value="false"
data-action="dragula-sortable:drop->confirm-reorder#requestConfirmation dragula-sortable:drag->confirm-reorder#prepareForRevertOnCancel save-sort-order->dragula-sortable#saveSortOrder"
data-confirm-reorder-target="dragula-sortable"
...
>