PageSourceSearch

https://sifrcrm.up.railway.app/assets/controllers/accordion_contro…84a7072902c1bc796d90fec26aedbee9054427d5.js

js sifrcrm.up.railway.app collected 2026-10-03 12:14:29 UTC 2,676 bytes, 108 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// Accordion controller for collapsible sections
4// Persists open/closed state to localStorage
5export default class extends Controller {
6  static targets = ["content", "icon"]
7  static values = {
8    id: String,
9    defaultOpen: { type: Boolean, default: true }
10  }
11
12  connect() {
13    // Restore state from localStorage or use default
14    const savedState = this.getSavedState()
15    if (savedState !== null) {
16      this.isOpen = savedState
17    } else {
18      this.isOpen = this.defaultOpenValue
19    }
20
21    // Apply initial state without animation
22    this.applyState(false)
23  }
24
25  toggle() {
26    this.isOpen = !this.isOpen
27    this.applyState(true)
28    this.saveState()
29  }
30
31  expand() {
32    if (!this.isOpen) {
33      this.isOpen = true
34      this.applyState(true)
35      this.saveState()
36    }
37  }
38
39  collapse() {
40    if (this.isOpen) {
41      this.isOpen = false
42      this.applyState(true)
43      this.saveState()
44    }
45  }
46
47  applyState(animate) {
48    if (!this.hasContentTarget) return
49
50    const content = this.contentTarget
51
52    if (animate) {
53      content.classList.add("transition-all", "duration-200", "ease-in-out")
54    } else {
55      content.classList.remove("transition-all", "duration-200", "ease-in-out")
56    }
57
58    if (this.isOpen) {
59      // Expand
60      content.style.maxHeight = content.scrollHeight + "px"
61      content.classList.remove("overflow-hidden")
62
63      // After transition, remove max-height to allow dynamic content
64      if (animate) {
65        setTimeout(() => {
66          content.style.maxHeight = "none"
67        }, 200)
68      } else {
69        content.style.maxHeight = "none"
70      }
71    } else {
72      // Collapse - first set explicit height, then animate to 0
73      if (animate) {
74        content.style.maxHeight = content.scrollHeight + "px"
75        content.classList.add("overflow-hidden")
76        // Force reflow
77        content.offsetHeight
78        content.style.maxHeight = "0px"
79      } else {
80        content.classList.add("overflow-hidden")
81        content.style.maxHeight = "0px"
82      }
83    }
84
85    // Rotate icon
86    if (this.hasIconTarget) {
87      if (this.isOpen) {
88        this.iconTarget.classList.remove("rotate-[-90deg]")
89      } else {
90        this.iconTarget.classList.add("rotate-[-90deg]")
91      }
92    }
93  }
94
95  getSavedState() {
96    if (!this.idValue) return null
97    const key = `accordion-${this.idValue}`
98    const saved = localStorage.getItem(key)
99    if (saved === null) return null
100    return saved === "true"
101  }
102
103  saveState() {
104    if (!this.idValue) return
105    const key = `accordion-${this.idValue}`
106    localStorage.setItem(key, this.isOpen.toString())
107  }
108};

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.