import Sortable from 'sortablejs'; import { ApplicationController } from './application_controller'; export class SortableController extends ApplicationController { declare readonly animationValue: number; declare readonly handleValue: string; declare readonly groupValue: string; #sortable?: Sortable; static values = { animation: Number, handle: String, group: String }; connect() { this.#sortable = new Sortable(this.element as HTMLElement, { ...this.defaultOptions, ...this.options }); this.onGlobal('sortable:sort', () => this.setEdgeClassNames()); } disconnect() { this.#sortable?.destroy(); } private onEnd({ item, newIndex }: { item: HTMLElement; newIndex?: number }) { if (newIndex == null) return; this.dispatch('end', { target: item, detail: { position: newIndex } }); this.setEdgeClassNames(); } setEdgeClassNames() { const items = this.element.children; for (const item of items) { item.classList.remove('first', 'last'); } if (items.length > 1) { const first = items[0]; const last = items[items.length - 1]; first?.classList.add('first'); last?.classList.add('last'); } } get options(): Sortable.Options { return { animation: this.animationValue || this.defaultOptions.animation || 150, handle: this.handleValue || this.defaultOptions.handle || undefined, group: this.groupValue || this.defaultOptions.group || undefined, onEnd: (event) => this.onEnd(event) }; } get defaultOptions(): Sortable.Options { return { fallbackOnBody: true, swapThreshold: 0.65 }; } }