Skip to content

Commit ce599c6

Browse files
committed
fix(Select): improve scroll behavior and content positioning
1 parent 320952a commit ce599c6

1 file changed

Lines changed: 11 additions & 21 deletions

File tree

packages/bits-ui/src/lib/bits/select/select.svelte.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ abstract class SelectBaseRootState {
112112
if (!this.highlightedNode) return null;
113113
return this.highlightedNode.getAttribute("data-label");
114114
});
115+
contentIsPositioned = $state(false);
115116
isUsingKeyboard = false;
116117
isCombobox = false;
117118
domContext = new DOMContext(() => null);
@@ -143,27 +144,8 @@ abstract class SelectBaseRootState {
143144
}
144145

145146
scrollHighlightedNodeIntoView(node: HTMLElement) {
146-
const scrollContainer = this.viewportNode ?? this.contentNode;
147-
if (!scrollContainer) return;
148-
149-
const scrollContainerRect = scrollContainer.getBoundingClientRect();
150-
const nodeRect = node.getBoundingClientRect();
151-
152-
if (this.opts.scrollAlignment.current === "center") {
153-
const scrollContainerCenter = scrollContainerRect.top + scrollContainerRect.height / 2;
154-
const nodeCenter = nodeRect.top + nodeRect.height / 2;
155-
scrollContainer.scrollTop += nodeCenter - scrollContainerCenter;
156-
return;
157-
}
158-
159-
if (nodeRect.top < scrollContainerRect.top) {
160-
scrollContainer.scrollTop -= scrollContainerRect.top - nodeRect.top;
161-
return;
162-
}
163-
164-
if (nodeRect.bottom > scrollContainerRect.bottom) {
165-
scrollContainer.scrollTop += nodeRect.bottom - scrollContainerRect.bottom;
166-
}
147+
if (!this.viewportNode || !this.contentIsPositioned) return;
148+
node.scrollIntoView({ block: this.opts.scrollAlignment.current });
167149
}
168150

169151
getCandidateNodes(): HTMLElement[] {
@@ -927,17 +909,24 @@ export class SelectContentState {
927909

928910
onDestroyEffect(() => {
929911
this.root.contentNode = null;
912+
this.root.contentIsPositioned = false;
930913
this.isPositioned = false;
931914
});
932915

933916
watch(
934917
() => this.root.opts.open.current,
935918
() => {
936919
if (this.root.opts.open.current) return;
920+
this.root.contentIsPositioned = false;
937921
this.isPositioned = false;
938922
}
939923
);
940924

925+
watch([() => this.isPositioned, () => this.root.highlightedNode], () => {
926+
if (!this.isPositioned || !this.root.highlightedNode) return;
927+
this.root.scrollHighlightedNodeIntoView(this.root.highlightedNode);
928+
});
929+
941930
this.onpointermove = this.onpointermove.bind(this);
942931
}
943932

@@ -1011,6 +1000,7 @@ export class SelectContentState {
10111000
// onPlaced is also called when the menu is closed, so we need to check if the menu
10121001
// is actually open to avoid setting positioning to true when the menu is closed
10131002
if (this.root.opts.open.current) {
1003+
this.root.contentIsPositioned = true;
10141004
this.isPositioned = true;
10151005
}
10161006
},

0 commit comments

Comments
 (0)