1import { Controller } from "@hotwired/stimulus" 2import { Turbo } from "@hotwired/turbo-rails" 3import Sortable from "sortablejs" 4 5// data-controller="sortable" 6// data-sortable-url-value="/path/to/reorder" (optional; PATCH with ids[]) 7// data-sortable-handle-value=".drag-handle" (CSS selector for drag handle) 8// data-sortable-item-selector-value="[data-block-id]" (optional; how to find items) 9// data-sortable-id-attribute-value="data-block-id" (attr to read for PATCH ids) 10// data-sortable-turbo-stream-value="true" (optional; render the response as 11// a Turbo Stream â for endpoints that have to hand back a fresh list, e.g. 12// because saving the new order changed the ids the next PATCH would send) 13export default class extends Controller { 14 static values = { 15 url: String, 16 handle: { type: String, default: "" }, 17 itemSelector: { type: String, default: "[data-block-id],[data-row-id]" }, 18 idAttribute: { type: String, default: "data-block-id" }, 19 turboStream: { type: Boolean, default: false } 20 } 21 22 connect() { 23 this.sortable = Sortable.create(this.element, { 24 handle: this.handleValue || undefined, 25 animation: 150, 26 ghostClass: "opacity-40", 27 onEnd: this.onEnd.bind(this) 28 }) 29 } 30 31 disconnect() { 32 if (this.sortable) this.sortable.destroy() 33 } 34 35 async onEnd() { 36 if (!this.urlValue) return 37 const ids = Array.from(this.element.querySelectorAll(this.itemSelectorValue)) 38 .map(el => el.getAttribute(this.idAttributeValue)) 39 .filter(Boolean) 40 const body = new FormData() 41 ids.forEach(id => body.append("ids[]", id)) 42 const accept = this.turboStreamValue ? "text/vnd.turbo-stream.html" : "application/json" 43 const response = await fetch(this.urlValue, { 44 method: "PATCH", 45 headers: { "X-CSRF-Token": this.csrf(), Accept: accept }, 46 body 47 }) 48 if (this.turboStreamValue && response.ok) { 49 Turbo.renderStreamMessage(await response.text()) 50 } 51 } 52 53 csrf() { 54 const m = document.querySelector('meta[name="csrf-token"]') 55 return m ? m.content : "" 56 } 57}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.