Skip to content

Instantly share code, notes, and snippets.

@anson0370
Created August 7, 2024 08:40
Show Gist options
  • Save anson0370/030156864702fb962bd8b5d505ea9a37 to your computer and use it in GitHub Desktop.
Save anson0370/030156864702fb962bd8b5d505ea9a37 to your computer and use it in GitHub Desktop.
fix shadcn Select component for mobile
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils"
const SelectContext = React.createContext<{ setOpen: (open: boolean) => void }>({
setOpen: () => {},
})
const Select: React.FC<SelectPrimitive.SelectProps> = ({
children,
open,
onOpenChange,
...props
}) => {
const [delayedOpen, setDelayedOpen] = React.useState(false)
const handleOpenChange = (newOpenState: boolean) => {
if (newOpenState) {
(onOpenChange ?? setDelayedOpen)(newOpenState)
} else {
// Delay closing by 50ms to avoid click-through to the underlying elements
setTimeout(() => {
(onOpenChange ?? setDelayedOpen)(newOpenState)
}, 50)
}
}
return (
// Wrap it with Context, then you can call setOpen inside the Trigger later
<SelectContext.Provider value={{
setOpen: handleOpenChange,
}}>
<SelectPrimitive.Root
open={open ?? delayedOpen}
onOpenChange={handleOpenChange}
{...props}
>
{children}
</SelectPrimitive.Root>
</SelectContext.Provider>
)
}
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & { disableIcon?: boolean }
>(({ className, children, disableIcon, onPointerDown, onPointerUp, ...props }, ref) => {
const { setOpen } = React.useContext(SelectContext);
return (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-gray-200 bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 dark:focus:ring-gray-300",
className
)}
// Prevent the default behavior for touch events to avoid triggering during a swipe
onPointerDown={(e) => {
if (onPointerDown) onPointerDown(e)
if (e.pointerType === 'touch') {
e.preventDefault()
}
}}
onPointerUp={(e) => {
if (onPointerUp) onPointerUp(e)
if (e.pointerType === 'touch') {
setOpen(true)
}
}}
{...props}
>
{children}
{!disableIcon && (
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 translate-x-1 opacity-50" />
</SelectPrimitive.Icon>
)}
</SelectPrimitive.Trigger>
);
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment