-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdropdown_controller.js
More file actions
80 lines (69 loc) · 2.41 KB
/
Copy pathdropdown_controller.js
File metadata and controls
80 lines (69 loc) · 2.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import { Controller } from "@hotwired/stimulus"
// Dropdown Controller for Stimulus
//
// Toggles a dropdown menu open and closed. Replaces Bootstrap's
// data-bs-toggle="dropdown" behavior.
//
// Expected HTML Structure:
// ------------------------
// <div class="dropdown" data-controller="dropdown">
// <button data-action="click->dropdown#toggle" aria-expanded="false">Open menu</button>
//
// <ul class="dropdown-menu" data-dropdown-target="menu">
// <li>...menu items...</li>
// </ul>
// </div>
//
// The menu needs no markup to start closed: the stylesheet's
// `.dropdown-menu` rule is `display: none`, and the controller adds the
// `show` class to reveal it. The menu also closes when clicking anywhere
// else on the page or pressing the Escape key.
//
// Hiding is class-based (never the HTML `hidden` attribute) on purpose:
// it keeps menu items present in the DOM for tests and for CSS-less
// contexts, mirroring how Bootstrap's dropdowns behaved.
export default class extends Controller {
static targets = ["menu"]
connect() {
this._open = false
// Bound versions of the handlers so we can remove the exact same
// listeners later
this._boundClickOutside = this._clickOutside.bind(this)
this._boundKeydown = this._keydown.bind(this)
}
toggle(event) {
event.preventDefault()
if (this._open) {
this.close()
} else {
this.open(event.currentTarget)
}
}
open(button) {
this._open = true
this.menuTarget.classList.add("show")
this.menuTarget.classList.remove("hidden")
button?.setAttribute("aria-expanded", "true")
// Close when clicking outside the dropdown or pressing Escape
document.addEventListener("click", this._boundClickOutside)
document.addEventListener("keydown", this._boundKeydown)
}
close() {
this._open = false
this.menuTarget.classList.remove("show")
this.menuTarget.classList.add("hidden")
this.element.querySelector("[aria-expanded]")?.setAttribute("aria-expanded", "false")
document.removeEventListener("click", this._boundClickOutside)
document.removeEventListener("keydown", this._boundKeydown)
}
_clickOutside(e) {
if (!this.element.contains(e.target)) this.close()
}
_keydown(e) {
if (e.key === "Escape") this.close()
}
disconnect() {
document.removeEventListener("click", this._boundClickOutside)
document.removeEventListener("keydown", this._boundKeydown)
}
}