x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<div class="card flex flex-col gap" style="max-inline-size: 400px" data-controller="sortable" data-parent-id="1" data-sortable-handle-value=".handle">
<div class="flex items-center gap border p-3" data-url-value="https://csszero.lazaronixon.com/sortables/10">
<img class="colorize-black handle" src="/assets/move-c3727cc8.svg" width="16" height="16" />
<span>Item 1</span>
</div>
<div class="flex items-center gap border p-3" data-url-value="https://csszero.lazaronixon.com/sortables/11">
<img class="colorize-black handle" src="/assets/move-c3727cc8.svg" width="16" height="16" />
<span>Item 2</span>
</div>
<div class="flex items-center gap border p-3" data-url-value="https://csszero.lazaronixon.com/sortables/12">
<img class="colorize-black handle" src="/assets/move-c3727cc8.svg" width="16" height="16" />
<span>Item 3</span>
</div>
<div class="flex items-center gap border p-3" data-url-value="https://csszero.lazaronixon.com/sortables/13">
<img class="colorize-black handle" src="/assets/move-c3727cc8.svg" width="16" height="16" />
<span>Item 4</span>
</div>
<div class="flex items-center gap border p-3" data-url-value="https://csszero.lazaronixon.com/sortables/14">
<img class="colorize-black handler" src="/assets/move-c3727cc8.svg" width="16" height="16" />
<span>Item 5</span>
</div>
<div class="flex items-center gap border p-3" data-url-value="https://csszero.lazaronixon.com/sortables/15">
<img class="colorize-black handle" src="/assets/move-c3727cc8.svg" width="16" height="16" />
<span>Item 6</span>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="card flex flex-col gap" style="max-inline-size: 400px" data-controller="sortable" data-parent-id="1" data-sortable-handle-value=".handle">
<%= tag.div class: "flex items-center gap border p-3", data: { url_value: main_app.sortable_url(10) } do %>
<%= image_tag "move.svg", class: "colorize-black handle", size: 16 %>
<span>Item 1</span>
<% end %>
<%= tag.div class: "flex items-center gap border p-3", data: { url_value: main_app.sortable_url(11) } do %>
<%= image_tag "move.svg", class: "colorize-black handle", size: 16 %>
<span>Item 2</span>
<% end %>
<%= tag.div class: "flex items-center gap border p-3", data: { url_value: main_app.sortable_url(12) } do %>
<%= image_tag "move.svg", class: "colorize-black handle", size: 16 %>
<span>Item 3</span>
<% end %>
<%= tag.div class: "flex items-center gap border p-3", data: { url_value: main_app.sortable_url(13) } do %>
<%= image_tag "move.svg", class: "colorize-black handle", size: 16 %>
<span>Item 4</span>
<% end %>
<%= tag.div class: "flex items-center gap border p-3", data: { url_value: main_app.sortable_url(14) } do %>
<%= image_tag "move.svg", class: "colorize-black handler", size: 16 %>
<span>Item 5</span>
<% end %>
<%= tag.div class: "flex items-center gap border p-3", data: { url_value: main_app.sortable_url(15) } do %>
<%= image_tag "move.svg", class: "colorize-black handle", size: 16 %>
<span>Item 6</span>
<% end %>
</div>
CSS is not required or multiple files are needed, check the notes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Controller } from "@hotwired/stimulus"
import { put } from "https://cdn.skypack.dev/@rails/request.js@0.0.11?min"
import Sortable from "https://cdn.skypack.dev/sortablejs?min"
export default class extends Controller {
static values = { url: String, group: String, handle: String }
connect() {
this.sortable = new Sortable(this.element, this.#options)
}
disconnect() {
this.sortable.destroy()
}
#submit({ item, newIndex, to }) {
put(item.dataset.urlValue, { query: { position: newIndex, parent_id: to.dataset.parentId } })
}
get #options() {
return { animation: 150, onAdd: this.#submit, onUpdate: this.#submit, group: this.groupValue, handle: this.handleValue }
}
}