Skip to content

Mobile multi select sheet#2750

Open
huumn wants to merge 2 commits intomasterfrom
mobile-multi-select-sheet
Open

Mobile multi select sheet#2750
huumn wants to merge 2 commits intomasterfrom
mobile-multi-select-sheet

Conversation

@huumn
Copy link
Member

@huumn huumn commented Jan 11, 2026

This is an attempt at creating a better multi-select UI for mobile. It's a full width scrollable modal "sheet" that comes up from the bottom when the select is clicked. You can drag it down, click off, hit 'done' on the keyboard, or make a selection to close it.

Simulator Screenshot - iPhone 15 Pro - 2026-01-10 at 23 34 27 Simulator Screenshot - iPhone 15 Pro - 2026-01-10 at 23 34 46

It works prrrretty great. It's entirely vibed though and I haven't reviewed it much.


Note

Implements a mobile-first multi-select experience and refines desktop behavior.

  • Adds useIsMobile and a mobile bottom-sheet for MultiSelect with searchable options, selection toggling, drag-to-close, backdrop click, keyboard-aware layout, and body scroll locking
  • Centralizes value updates via handleChange, plus helpers (toggleOption, removeValue, clearAll); preserves onChange(formik, values) behavior
  • Desktop: sets maxMenuHeight/menuPlacement, reverses indicators via custom IndicatorsContainer, and keeps custom label/remove components
  • CSS overhaul in multi-select.module.css: mobile control/overlay/sheet styles, touch target sizing, indicator/tag layout updates, hover-safe media queries, and menu/option truncation improvements

Written by Cursor Bugbot for commit 6b9f2d5. This will update automatically on new commits. Configure here.

// Delay to allow click events to fire first (longer for Android)
setTimeout(() => onClose(), 150)
}
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale timeout causes sheet to close unexpectedly

Medium Severity

The setTimeout(() => onClose(), 150) in the search input's onBlur handler is never cleaned up. If a user closes the bottom sheet (e.g., by selecting an option) and quickly reopens it within 150ms, the stale timeout will fire and call onClose(), unexpectedly closing the newly opened sheet. The timeout reference needs to be stored in a ref and cleared when the sheet closes or when the component unmounts.

Fix in Cursor Fix in Web

const { children, ...rest } = props
// children is an array: [ClearIndicator, IndicatorSeparator, DropdownIndicator]
// Reverse to put dropdown first, then clear on the outside
const reversed = children ? [...children].reverse() : children
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spreading children may fail for single element

Low Severity

The [...children].reverse() pattern assumes children is always an array. In React, when there's exactly one child, props.children is that single React element rather than an array. Spreading a non-iterable React element would throw a TypeError. Using React.Children.toArray(children).reverse() handles all cases safely, as demonstrated elsewhere in the codebase in table-of-contents.js.

Fix in Cursor Fix in Web

document.body.style.left = ''
document.body.style.right = ''
document.body.style.overflow = ''
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scroll position lost when component unmounts while open

Medium Severity

The cleanup function for the scroll-lock effect resets body styles but doesn't restore the scroll position. When isOpen transitions from true to false, scroll is properly restored in the else branch. However, if the component unmounts while the sheet is open (e.g., via navigation), only the cleanup runs - it clears document.body.style.top without first reading the stored scroll value and calling window.scrollTo(). This causes the page to jump to the top when the user navigates away with the sheet open.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant