Skip to content

Commit 1558f3c

Browse files
committed
Add auto update to keep dropdown positioned correctly
1 parent e6f1932 commit 1558f3c

File tree

1 file changed

+29
-14
lines changed

1 file changed

+29
-14
lines changed

lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Controller } from "@hotwired/stimulus";
2-
import { computePosition, flip, shift, offset } from "@floating-ui/dom";
2+
import { computePosition, flip, shift, offset, autoUpdate } from "@floating-ui/dom";
33

44
export default class extends Controller {
55
static targets = ["trigger", "content", "menuItem"];
@@ -12,11 +12,20 @@ export default class extends Controller {
1212
type: Object,
1313
default: {},
1414
},
15-
}
15+
};
1616

1717
connect() {
1818
this.boundHandleKeydown = this.#handleKeydown.bind(this); // Bind the function so we can remove it later
1919
this.selectedIndex = -1;
20+
this.cleanup = autoUpdate(
21+
this.triggerTarget,
22+
this.contentTarget,
23+
this.#computeTooltip.bind(this),
24+
);
25+
}
26+
27+
disconnect() {
28+
this.cleanup();
2029
}
2130

2231
#computeTooltip() {
@@ -40,14 +49,16 @@ export default class extends Controller {
4049
}
4150

4251
toggle() {
43-
this.contentTarget.classList.contains("hidden") ? this.#open() : this.close();
52+
this.contentTarget.classList.contains("hidden")
53+
? this.#open()
54+
: this.close();
4455
}
4556

4657
#open() {
4758
this.openValue = true;
4859
this.#deselectAll();
4960
this.#addEventListeners();
50-
this.#computeTooltip()
61+
this.#computeTooltip();
5162
this.contentTarget.classList.remove("hidden");
5263
}
5364

@@ -59,15 +70,17 @@ export default class extends Controller {
5970

6071
#handleKeydown(e) {
6172
// return if no menu items (one line fix for)
62-
if (this.menuItemTargets.length === 0) { return; }
73+
if (this.menuItemTargets.length === 0) {
74+
return;
75+
}
6376

64-
if (e.key === 'ArrowDown') {
77+
if (e.key === "ArrowDown") {
6578
e.preventDefault();
6679
this.#updateSelectedItem(1);
67-
} else if (e.key === 'ArrowUp') {
80+
} else if (e.key === "ArrowUp") {
6881
e.preventDefault();
6982
this.#updateSelectedItem(-1);
70-
} else if (e.key === 'Enter' && this.selectedIndex !== -1) {
83+
} else if (e.key === "Enter" && this.selectedIndex !== -1) {
7184
e.preventDefault();
7285
this.menuItemTargets[this.selectedIndex].click();
7386
}
@@ -76,7 +89,7 @@ export default class extends Controller {
7689
#updateSelectedItem(direction) {
7790
// Check if any of the menuItemTargets have aria-selected="true" and set the selectedIndex to that index
7891
this.menuItemTargets.forEach((item, index) => {
79-
if (item.getAttribute('aria-selected') === 'true') {
92+
if (item.getAttribute("aria-selected") === "true") {
8093
this.selectedIndex = index;
8194
}
8295
});
@@ -99,22 +112,24 @@ export default class extends Controller {
99112
#toggleAriaSelected(element, isSelected) {
100113
// Add or remove attribute
101114
if (isSelected) {
102-
element.setAttribute('aria-selected', 'true');
115+
element.setAttribute("aria-selected", "true");
103116
} else {
104-
element.removeAttribute('aria-selected');
117+
element.removeAttribute("aria-selected");
105118
}
106119
}
107120

108121
#deselectAll() {
109-
this.menuItemTargets.forEach(item => this.#toggleAriaSelected(item, false));
122+
this.menuItemTargets.forEach((item) =>
123+
this.#toggleAriaSelected(item, false),
124+
);
110125
this.selectedIndex = -1;
111126
}
112127

113128
#addEventListeners() {
114-
document.addEventListener('keydown', this.boundHandleKeydown);
129+
document.addEventListener("keydown", this.boundHandleKeydown);
115130
}
116131

117132
#removeEventListeners() {
118-
document.removeEventListener('keydown', this.boundHandleKeydown);
133+
document.removeEventListener("keydown", this.boundHandleKeydown);
119134
}
120135
}

0 commit comments

Comments
 (0)