ui icon indicating copy to clipboard operation
ui copied to clipboard

Select Input Opening Automatically on Mobile Scroll

Open HashenUdara opened this issue 1 year ago • 1 comments
trafficstars

I have developed a web application using shadcn-ui and Next.js, featuring a registration page with a height exceeding the screen height, requiring users to scroll down to complete the full registration form.

The issue arises when users scroll on the page using a mobile phone. The 'select' input automatically triggers the touch finder, exhibiting oversensitivity. This results in the 'select' inputs auto-opening when users attempt to scroll down. It occurs when a user touches the 'select' input while scrolling. It's important to note that I am using the Shadcn-UI select without any custom modifications.

Is there any way to control the auto-opening of 'select' inputs when scrolling on mobile devices?

Live link for reference: https://x-project-design-system.vercel.app/reg

HashenUdara avatar Feb 03 '24 08:02 HashenUdara

I noticed this, I'm looking for a solution to solve this behavior. Please, lemme know.

maykon-oliveira avatar Feb 03 '24 12:02 maykon-oliveira

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.

shadcn avatar Feb 25 '24 23:02 shadcn

hey there, is there any solution for this?

erkamdemirci avatar Mar 15 '24 19:03 erkamdemirci

Any fixes for this yet?

MikeDrenth avatar Mar 27 '24 08:03 MikeDrenth

As a workaround I added this component

const SelectWithTrigger = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>
>(({ children, ...props }, ref) => {
  const [isOpen, setIsOpen] = React.useState(false);
  return (
    <SelectPrimitive.Root open={isOpen} onOpenChange={setIsOpen} {...props} >
      <SelectPrimitive.Trigger
        onPointerDown={(e) => e.preventDefault()}
        onClick={() => {
          setIsOpen((state) => !state);
        }}
        ref={ref}
        className={cn(
          "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
        )}
      >
        {children}
        <SelectPrimitive.Icon asChild>
          <CaretSortIcon className="h-4 w-4 opacity-50" />
        </SelectPrimitive.Icon>
      </SelectPrimitive.Trigger>
    </SelectPrimitive.Root>
  );
});

SelectWithTrigger.displayName = "SelectWithTrigger";

Just make sure to export it and in your component replace

<Select>
   <SelectTrigger>
      <SelectValue />
   </SelectTrigger>
   
   <SelectContent></SelectContent>
</Select>

With

<SelectWithTrigger>
   <SelectValue />
   <SelectContent></SelectContent>
</SelectWithTrigger>

cervantes-x avatar Apr 19 '24 11:04 cervantes-x

As a workaround I added this component

const SelectWithTrigger = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>
>(({ children, ...props }, ref) => {
  const [isOpen, setIsOpen] = React.useState(false);
  return (
    <SelectPrimitive.Root open={isOpen} onOpenChange={setIsOpen} {...props} >
      <SelectPrimitive.Trigger
        onPointerDown={(e) => e.preventDefault()}
        onClick={() => {
          setIsOpen((state) => !state);
        }}
        ref={ref}
        className={cn(
          "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
        )}
      >
        {children}
        <SelectPrimitive.Icon asChild>
          <CaretSortIcon className="h-4 w-4 opacity-50" />
        </SelectPrimitive.Icon>
      </SelectPrimitive.Trigger>
    </SelectPrimitive.Root>
  );
});

SelectWithTrigger.displayName = "SelectWithTrigger";

Just make sure to export it and in your component replace

<Select>
   <SelectTrigger>
      <SelectValue />
   </SelectTrigger>
   
   <SelectContent></SelectContent>
</Select>

With

<SelectWithTrigger>
   <SelectValue />
   <SelectContent></SelectContent>
</SelectWithTrigger>

This is working perfectly. Now the problem is solved. Thank you 🙌🏻

HashenUdara avatar Apr 29 '24 18:04 HashenUdara

I don't know you guys, but I lose the accessibility.

image

This is happening because the FormControl that wraps the SelectValue is setting the ID attribute on the span, not on the button. Did someone catch that situation?

maykon-oliveira avatar May 01 '24 00:05 maykon-oliveira

As a workaround I added this component

const SelectWithTrigger = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>
>(({ children, ...props }, ref) => {
  const [isOpen, setIsOpen] = React.useState(false);
  return (
    <SelectPrimitive.Root open={isOpen} onOpenChange={setIsOpen} {...props} >
      <SelectPrimitive.Trigger
        onPointerDown={(e) => e.preventDefault()}
        onClick={() => {
          setIsOpen((state) => !state);
        }}
        ref={ref}
        className={cn(
          "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
        )}
      >
        {children}
        <SelectPrimitive.Icon asChild>
          <CaretSortIcon className="h-4 w-4 opacity-50" />
        </SelectPrimitive.Icon>
      </SelectPrimitive.Trigger>
    </SelectPrimitive.Root>
  );
});

SelectWithTrigger.displayName = "SelectWithTrigger";

Just make sure to export it and in your component replace

<Select>
   <SelectTrigger>
      <SelectValue />
   </SelectTrigger>
   
   <SelectContent></SelectContent>
</Select>

With

<SelectWithTrigger>
   <SelectValue />
   <SelectContent></SelectContent>
</SelectWithTrigger>

I was having similar issues, and this worked like a charm, thank you! added this to keep the pointer functionality on non-touch devices:

in SelectWithTrigger:

  const isTouchDevice = () => {
    return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
  };

in <SelectPrimitive.Trigger:

      onPointerDown={(e) => {
        if (isTouchDevice()) {
          e.preventDefault();
        }
      }}
      onClick={() => {
        if (isTouchDevice()) {
          setIsOpen((state) => !state);
        }
      }}

potatiumstotalis avatar May 05 '24 00:05 potatiumstotalis

still facing this, when user scroll it naturally touch the screen and move it, if it happened that touches the select input it will open and block everything

now no scroll, and input opens out of context ...

nabildroid avatar Jul 30 '24 18:07 nabildroid