PageSourceSearch

https://www.millsborochamber.com/assets/controllers/benefit_reorder_controller-dd2c399a.js

js millsborochamber.com collected 2026-09-29 09:49:56 UTC 6,240 bytes, 221 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3export default class extends Controller {
4  static targets = ["item", "list", "moveDown", "moveUp", "position", "status"]
5
6  connect() {
7    this.originalOrder = this.itemTargets.map((item) => item.dataset.benefitId)
8    this.nativeDrag = null
9    this.pointerDrag = null
10    this.previousBodyUserSelect = null
11    this.updateControls()
12  }
13
14  disconnect() {
15    this.finishNativeDrag()
16    this.finishPointerDrag()
17  }
18
19  moveUp(event) {
20    const item = event.currentTarget.closest("[data-benefit-reorder-target='item']")
21    const previousItem = item?.previousElementSibling
22    if (!item || !previousItem) return
23
24    previousItem.before(item)
25    this.updateControls(item)
26    this.focusMoveControl(item, event.currentTarget)
27  }
28
29  moveDown(event) {
30    const item = event.currentTarget.closest("[data-benefit-reorder-target='item']")
31    const nextItem = item?.nextElementSibling
32    if (!item || !nextItem) return
33
34    nextItem.after(item)
35    this.updateControls(item)
36    this.focusMoveControl(item, event.currentTarget)
37  }
38
39  focusMoveControl(item, control) {
40    if (!control.disabled) {
41      control.focus()
42      return
43    }
44    item.querySelector("[data-benefit-reorder-target='moveUp']:not([disabled]), [data-benefit-reorder-target='moveDown']:not([disabled])")?.focus()
45  }
46
47  noop(event) {
48    event.preventDefault()
49  }
50
51  dragStart(event) {
52    const item = event.currentTarget.closest("[data-benefit-reorder-target='item']")
53    if (!item || this.itemTargets.length <= 1) {
54      event.preventDefault()
55      return
56    }
57
58    this.finishPointerDrag()
59    this.nativeDrag = { item, moved: false }
60    this.showDragging(item)
61
62    event.dataTransfer.effectAllowed = "move"
63    event.dataTransfer.setData("text/plain", item.dataset.benefitId)
64  }
65
66  dragOver(event) {
67    if (!this.nativeDrag) return
68
69    event.preventDefault()
70    event.dataTransfer.dropEffect = "move"
71
72    const over = event.target.closest("[data-benefit-reorder-target='item']")
73    if (this.moveDraggedItem(this.nativeDrag, over, event.clientY)) this.updateControls()
74  }
75
76  drop(event) {
77    if (!this.nativeDrag) return
78
79    event.preventDefault()
80    this.finishNativeDrag()
81  }
82
83  dragEnd() {
84    this.finishNativeDrag()
85  }
86
87  startPointerDrag(event) {
88    if (event.pointerType === "mouse") return
89    if (event.button !== undefined && event.button !== 0) return
90
91    const handle = event.currentTarget
92    const item = handle.closest("[data-benefit-reorder-target='item']")
93    if (!item || this.itemTargets.length <= 1) return
94
95    event.preventDefault()
96    this.finishNativeDrag()
97    this.finishPointerDrag()
98
99    this.pointerDrag = {
100      pointerId: event.pointerId,
101      handle,
102      item,
103      moved: false
104    }
105
106    if (handle.setPointerCapture) handle.setPointerCapture(event.pointerId)
107    this.showDragging(item)
108    this.previousBodyUserSelect = document.body.style.userSelect
109    document.body.style.userSelect = "none"
110  }
111
112  dragPointer(event) {
113    if (!this.pointerDrag || event.pointerId !== this.pointerDrag.pointerId) return
114
115    event.preventDefault()
116    const over = document.elementFromPoint(event.clientX, event.clientY)?.closest?.(
117      "[data-benefit-reorder-target='item']"
118    )
119    if (this.moveDraggedItem(this.pointerDrag, over, event.clientY)) this.updateControls()
120  }
121
122  endPointerDrag(event) {
123    if (!this.pointerDrag || event.pointerId !== this.pointerDrag.pointerId) return
124
125    event.preventDefault()
126    this.finishPointerDrag()
127  }
128
129  reset() {
130    if (!this.hasListTarget) return
131
132    this.finishNativeDrag()
133    this.finishPointerDrag()
134
135    const itemsById = new Map(this.itemTargets.map((item) => [item.dataset.benefitId, item]))
136    this.originalOrder.forEach((id) => {
137      const item = itemsById.get(id)
138      if (item) this.listTarget.append(item)
139    })
140    this.updateControls()
141    if (this.hasStatusTarget) this.statusTarget.textContent = ""
142  }
143
144  finishNativeDrag() {
145    if (!this.nativeDrag) return
146
147    const { item, moved } = this.nativeDrag
148    this.hideDragging(item)
149    this.nativeDrag = null
150
151    if (moved) this.updateControls(item)
152  }
153
154  finishPointerDrag() {
155    if (!this.pointerDrag) return
156
157    const { handle, pointerId, item, moved } = this.pointerDrag
158
159    if (handle?.releasePointerCapture && handle.hasPointerCapture?.(pointerId)) {
160      handle.releasePointerCapture(pointerId)
161    }
162
163    this.hideDragging(item)
164
165    if (this.previousBodyUserSelect === null) {
166      document.body.style.removeProperty("user-select")
167    } else {
168      document.body.style.userSelect = this.previousBodyUserSelect
169    }
170    this.previousBodyUserSelect = null
171    this.pointerDrag = null
172
173    if (moved) this.updateControls(item)
174  }
175
176  moveDraggedItem(drag, over, clientY) {
177    const item = drag.item
178    if (!over || over === item || over.parentElement !== this.listTarget) return false
179
180    const rect = over.getBoundingClientRect()
181    const insertBefore = clientY < rect.top + rect.height / 2
182
183    if (insertBefore) {
184      if (item === over.previousElementSibling) return false
185      over.before(item)
186    } else {
187      if (item === over.nextElementSibling) return false
188      over.after(item)
189    }
190
191    drag.moved = true
192    return true
193  }
194
195  showDragging(item) {
196    item.classList.add("opacity-80", "ring-2", "ring-amber-500/20")
197  }
198
199  hideDragging(item) {
200    item.classList.remove("opacity-80", "ring-2", "ring-amber-500/20")
201  }
202
203  updateControls(movedItem = null) {
204    const items = this.itemTargets
205
206    items.forEach((item, index) => {
207      const position = item.querySelector("[data-benefit-reorder-target='position']")
208      const moveUp = item.querySelector("[data-benefit-reorder-target='moveUp']")
209      const moveDown = item.querySelector("[data-benefit-reorder-target='moveDown']")
210
211      if (position) position.textContent = index + 1
212      if (moveUp) moveUp.disabled = index === 0
213      if (moveDown) moveDown.disabled = index === items.length - 1
214    })
215
216    if (movedItem && this.hasStatusTarget) {
217      const position = items.indexOf(movedItem) + 1
218      this.statusTarget.textContent = `${movedItem.dataset.benefitName} moved to position ${position} of ${items.length}.`
219    }
220  }
221}

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.