Skip to content

Commit

Permalink
fix: Combobox click events firing on elements behind the content (#1109)
Browse files Browse the repository at this point in the history
* fix: `Combobox` click events firing on elements behind the content

* add changeset
  • Loading branch information
huntabyte authored Feb 6, 2025
1 parent 1668a23 commit 3635d93
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-cars-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"bits-ui": patch
---

fix: click events firing on elements behind the content when selecting Combobox.Item
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,4 @@
</div>
{/if}

<Mounted
onMountedChange={(m) => {
itemState.mounted = m;
}}
/>
<Mounted bind:mounted={itemState.mounted} />
40 changes: 28 additions & 12 deletions packages/bits-ui/src/lib/bits/select/select.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,25 @@ class SelectItemState {
this.onpointermove = this.onpointermove.bind(this);
}

handleSelect() {
if (this.opts.disabled.current) return;
const isCurrentSelectedValue = this.opts.value.current === this.root.opts.value.current;

// if allowDeselect is false and the item is already selected and we're not in a
// multi select, do nothing and close the menu
if (!this.root.opts.allowDeselect.current && isCurrentSelectedValue && !this.root.isMulti) {
this.root.handleClose();
return;
}

// otherwise, toggle the item and if we're not in a multi select, close the menu
this.root.toggleItem(this.opts.value.current, this.opts.label.current);

if (!this.root.isMulti && !isCurrentSelectedValue) {
this.root.handleClose();
}
}

snippetProps = $derived.by(() => ({
selected: this.isSelected,
highlighted: this.isHighlighted,
Expand All @@ -831,24 +850,21 @@ class SelectItemState {
* multiple clicks.
*/
onpointerup(e: BitsPointerEvent) {
if (e.defaultPrevented) return;
if (e.defaultPrevented || !this.opts.ref.current) return;
// prevent any default behavior
e.preventDefault();
if (this.opts.disabled.current) return;
const isCurrentSelectedValue = this.opts.value.current === this.root.opts.value.current;

// if allowDeselect is false and the item is already selected and we're not in a
// multi select, do nothing and close the menu
if (!this.root.opts.allowDeselect.current && isCurrentSelectedValue && !this.root.isMulti) {
this.root.handleClose();
/**
* For one reason or another, when it's a touch pointer and _only_ in combobox mode,
* we need to listen for the immediate click event to handle the selection, otherwise
* a click event will be fired on the element behind the item after the content closes.
*/
if (e.pointerType === "touch") {
on(this.opts.ref.current, "click", () => this.handleSelect(), { once: true });
return;
}

// otherwise, toggle the item and if we're not in a multi select, close the menu
this.root.toggleItem(this.opts.value.current, this.opts.label.current);
if (!this.root.isMulti && !isCurrentSelectedValue) {
this.root.handleClose();
}
this.handleSelect();
}

onpointermove(_: BitsPointerEvent) {
Expand Down

0 comments on commit 3635d93

Please sign in to comment.