PageSourceSearch

https://fluencypay.ai/assets/backend/collection_picker_controller-…dc4e8a6e525496c981af8963786f82a98f46c0f5.js

js fluencypay.ai collected 2026-10-05 15:34:08 UTC 3,584 bytes, 135 lines download raw bytes

1/* global Turbo */
2import { Controller } from '@hotwired/stimulus'
3import { post, destroy } from '@rails/request.js'
4
5export default class extends Controller {
6  static targets = ['dropdown', 'searchInput', 'collectionsList']
7  static values = {
8    productId: Number,
9    addUrl: String,
10    removeUrl: String,
11    isOpen: { type: Boolean, default: false }
12  }
13
14  connect () {
15    this.boundHandleClickOutside = this.handleClickOutside.bind(this)
16    this.boundHandleEscape = this.handleEscape.bind(this)
17
18    // Restore open state if it was open before re-render
19    if (this.isOpenValue) {
20      this.open()
21    }
22  }
23
24  disconnect () {
25    document.removeEventListener('click', this.boundHandleClickOutside)
26    document.removeEventListener('keydown', this.boundHandleEscape)
27  }
28
29  toggle (event) {
30    event.preventDefault()
31    event.stopPropagation()
32
33    if (this.dropdownTarget.classList.contains('hidden')) {
34      this.open()
35    } else {
36      this.close()
37    }
38  }
39
40  open () {
41    if (this.hasDropdownTarget) {
42      this.dropdownTarget.classList.remove('hidden')
43    }
44    this.searchInputTarget.value = ''
45    this.filterCollections('')
46    this.searchInputTarget.focus()
47    this.isOpenValue = true
48
49    // Add click outside listener after a small delay to avoid immediate closure
50    setTimeout(() => {
51      document.addEventListener('click', this.boundHandleClickOutside)
52      document.addEventListener('keydown', this.boundHandleEscape)
53    }, 10)
54  }
55
56  close () {
57    if (this.hasDropdownTarget) {
58      this.dropdownTarget.classList.add('hidden')
59    }
60    this.isOpenValue = false
61    document.removeEventListener('click', this.boundHandleClickOutside)
62    document.removeEventListener('keydown', this.boundHandleEscape)
63  }
64
65  handleClickOutside (event) {
66    if (!this.element.contains(event.target)) {
67      this.close()
68    }
69  }
70
71  handleEscape (event) {
72    if (event.key === 'Escape') {
73      this.close()
74    }
75  }
76
77  search (event) {
78    const query = event.target.value.toLowerCase()
79    this.filterCollections(query)
80  }
81
82  filterCollections (query) {
83    const items = this.collectionsListTarget.querySelectorAll('[data-collection-item]')
84
85    items.forEach(item => {
86      const name = item.dataset.collectionName.toLowerCase()
87      if (name.includes(query)) {
88        item.classList.remove('hidden')
89      } else {
90        item.classList.add('hidden')
91      }
92    })
93  }
94
95  async selectCollection (event) {
96    event.preventDefault()
97    event.stopPropagation()
98
99    const collectionId = event.currentTarget.dataset.collectionId
100    const isAdded = event.currentTarget.dataset.isAdded === 'true'
101    const button = event.currentTarget
102
103    // Disable button to prevent double clicks
104    button.disabled = true
105
106    try {
107      const url = isAdded ? this.removeUrlValue : this.addUrlValue
108      const requestMethod = isAdded ? destroy : post
109
110      const response = await requestMethod(url, {
111        headers: {
112          Accept: 'text/vnd.turbo-stream.html'
113        },
114        body: JSON.stringify({
115          collection_id: collectionId
116        })
117      })
118
119      if (response.ok) {
120        const html = await response.text
121        Turbo.renderStreamMessage(html)
122
123        // Dispatch event
124        const eventName = isAdded ? 'removed' : 'added'
125        this.dispatch(eventName, { detail: { collectionId } })
126      } else {
127        console.error('Failed to update collection')
128        button.disabled = false
129      }
130    } catch (error) {
131      console.error('Error updating collection:', error)
132      button.disabled = false
133    }
134  }
135};

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.