PageSourceSearch

https://gls.ai/assets/controllers/accordion_menu_controller-56f6dd…cf0b590bdef0f008547185554d4df16248c64ed8.js

js gls.ai collected 2026-10-05 14:57:26 UTC 3,573 bytes, 136 lines download raw bytes

1import { Controller } from '@hotwired/stimulus';
2import { enter, leave } from 'el-transition';
3
4export default class extends Controller {
5  static targets = ['submenu', 'caret'];
6  static values = { isOpen: Boolean, isSidebarCollapsed: Boolean, hasActiveItems: Boolean };
7
8  connect() {
9    requestAnimationFrame(() => {
10      this.initializeAccordion();
11      this.listenForEvents();
12    });
13  }
14
15  disconnect() {
16    this.stopListeningForEvents();
17  }
18
19  initializeAccordion() {
20    this.detectActiveLinks();
21    this.updateAccordionState();
22  }
23
24  listenForEvents() {
25    this.documentClickHandler = this.handleOutsideClick.bind(this);
26    document.addEventListener('click', this.documentClickHandler);
27
28    this.resizeHandler = this.handleWindowResize.bind(this);
29    window.addEventListener('resize', this.resizeHandler);
30
31    // Listens sidebar state changes:
32    this.sidebarStateHandler = this.handleSidebarStateChange.bind(this);
33    window.addEventListener('sidebar:state-changed', this.sidebarStateHandler);
34  }
35
36  stopListeningForEvents() {
37    document.removeEventListener('click', this.documentClickHandler);
38    window.removeEventListener('resize', this.resizeHandler);
39    window.removeEventListener('sidebar:state-changed', this.sidebarStateHandler);
40  }
41
42  handleOutsideClick(event) {
43    if (this.element.contains(event.target)) return;
44
45    const clickedLink = event.target.closest('a[data-sidebar-target="link"]');
46    if (clickedLink && this.submenuTarget.contains(clickedLink)) return;
47
48    if (this.isOpenValue) this.closeSubmenu();
49  }
50
51  handleWindowResize() {
52    this.detectActiveLinks();
53    this.updateAccordionState();
54  }
55
56  handleSidebarStateChange(event) {
57    this.isSidebarCollapsedValue = event.detail.isCollapsed;
58
59    if (this.isSidebarCollapsedValue && !this.isMobileView()) {
60      this.closeSubmenu();
61    } else {
62      this.detectActiveLinks();
63      this.updateAccordionState();
64    }
65  }
66
67  toggle(event) {
68    event.stopPropagation();
69
70    if (!this.isMobileView() && this.isSidebarCollapsedValue) {
71      this.expandSidebar();
72      return;
73    }
74
75    if (this.isSidebarCollapsedValue && !this.isMobileView()) return;
76
77    this.toggleSubmenu();
78  }
79
80  toggleSubmenu() {
81    this.isOpenValue = !this.isOpenValue;
82    this.updateAccordionState();
83  }
84
85  closeSubmenu() {
86    this.isOpenValue = false;
87    this.updateAccordionState();
88  }
89
90  isMobileView() {
91    return window.innerWidth < 1024;
92  }
93
94  detectActiveLinks() {
95    if (this.isSidebarCollapsedValue && !this.isMobileView()) {
96      this.isOpenValue = false;
97      return;
98    }
99
100    if (this.hasActiveItemsValue && (!this.isSidebarCollapsedValue || this.isMobileView())) {
101      this.isOpenValue = true;
102    }
103  }
104
105  updateAccordionState() {
106    this.isOpenValue ? enter(this.submenuTarget) : leave(this.submenuTarget);
107    this.updateCaretDirection();
108  }
109
110  updateCaretDirection() {
111    if (!this.hasCaretTarget) return;
112
113    this.caretTarget.classList.add(
114      'transition-transform',
115      'duration-[400ms]',
116      'ease-[cubic-bezier(0.49,-0.28,0.23,1.38)]'
117    );
118
119    this.caretTarget.classList.toggle('rotate-180', this.isOpenValue);
120    this.updateCaretColor();
121  }
122
123  updateCaretColor() {
124    if (this.hasActiveItemsValue) {
125      this.caretTarget.classList.add('text-white');
126      this.caretTarget.classList.remove('text-gray-200');
127    } else {
128      this.caretTarget.classList.remove('text-white');
129      this.caretTarget.classList.add('text-gray-200');
130    }
131  }
132
133  expandSidebar() {
134    window.dispatchEvent(new CustomEvent('show-menu'));
135  }
136};

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.