ui icon indicating copy to clipboard operation
ui copied to clipboard

Select options are truncated if they don't fit on the page

Open multiwebinc opened this issue 2 years ago • 10 comments

If you have lots of options, they get truncated:

<Select>
  <SelectTrigger className="w-[180px]">
    <SelectValue placeholder="Theme" />
  </SelectTrigger>
  <SelectContent>
    {[...Array(100)].map((e, i) => (
      <SelectItem key={i} value={`${i}`}>
        Item number {i}
      </SelectItem>
    ))}
  </SelectContent>
</Select>

image

There is no way to scroll the elements, however Radix does scroll them. When there isn't enough room it adds a vertical scroll thing:

image

multiwebinc avatar Apr 24 '23 23:04 multiwebinc

Just came here to report that. Also, looks like it only happens when SelectContent position prop is equal to popper which is the default. Besides that, to make the arrows appear when position is equal to item-aligned, you need to add SelectPrimitive.ScrollUpButton and SelectPrimitive.ScrollDownButton to the SelectContent component.

Related: https://github.com/radix-ui/primitives/issues/1980#issuecomment-1479692521

focux avatar Apr 25 '23 18:04 focux

I can add scrolling to <Select />. However I'd recommend checking out https://ui.shadcn.com/docs/components/command#combobox for large sets.

shadcn avatar Apr 26 '23 06:04 shadcn

ty @focux -- exactly what i was looking for. @shadcn would be cool to have it in the example docs. My use case is the timezone select, which I wouldn't want the command box for tbh.

btw, what would be the best way to integrate input for fuzzy search?

assimovt avatar May 22 '23 14:05 assimovt

You can add the following Tailwind class if position is set to popper to make it not exceed the viewport height:

<Select.Content className="max-h-[--radix-select-content-available-height]" />

piotrkulpinski avatar May 25 '23 06:05 piotrkulpinski

In the end, I added a @radix-ui/react-scroll-area to the component, and it seems to work for me. If anyone wants to use it, you just have to add the colors back since I'm using the Skeleton UI color tokens in my app (Click here to find out how I did that).

"use client"

import * as React from "react"
import * as ScrollArea from "@radix-ui/react-scroll-area"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown } from "lucide-react"

import { cn } from "@/lib/utils"

const Select = SelectPrimitive.Root

const SelectGroup = SelectPrimitive.Group

const SelectValue = SelectPrimitive.Value

const SelectTrigger = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Trigger
    ref={ref}
    className={cn(
      "select flex h-10 w-full items-center justify-between px-3 py-2 text-sm text-on-surface-token focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
      className
    )}
    {...props}
  >
    {children}
    <SelectPrimitive.Icon asChild>
      <ChevronDown className="h-4 w-4 opacity-50" />
    </SelectPrimitive.Icon>
  </SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName

const SelectContent = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
  <SelectPrimitive.Portal>
    <SelectPrimitive.Content
      ref={ref}
      className={cn(
        "relative z-50 min-w-[8rem] rounded-md border bg-surface-500 shadow-md animate-in fade-in-80",
        position === "popper" && "translate-y-1",
        className
      )}
      position={position}
      {...props}
    >
      <ScrollArea.Root type="auto">
        <SelectPrimitive.Viewport
          className={cn(
            "max-h-[40vh] p-1",
            position === "popper" &&
              "w-full min-w-[var(--radix-select-trigger-width)]"
          )}
          asChild
        >
          {/*
            TODO: Remove the inline style when https://github.com/radix-ui/primitives/issues/2059 is fixed
          */}
          <ScrollArea.Viewport style={{ overflowY: undefined }}>
            {children}
          </ScrollArea.Viewport>
        </SelectPrimitive.Viewport>
        <ScrollArea.Scrollbar
          className="w-3 px-1 py-2 bg-surface-300-600-token"
          orientation="vertical"
        >
          <ScrollArea.Thumb className="rounded-sm bg-surface-200-700-token" />
        </ScrollArea.Scrollbar>
      </ScrollArea.Root>
    </SelectPrimitive.Content>
  </SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName

const SelectLabel = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
  <SelectPrimitive.Label
    ref={ref}
    className={cn("py-1.5 pl-8 pr-2 font-semibold", className)}
    {...props}
  />
))
SelectLabel.displayName = SelectPrimitive.Label.displayName

const SelectItem = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Item
    ref={ref}
    className={cn(
      "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-surface-200-700-token data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[state='checked']:bg-surface-200-700-token",
      className
    )}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <SelectPrimitive.ItemIndicator>
        <Check className="h-4 w-4" />
      </SelectPrimitive.ItemIndicator>
    </span>

    <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
  </SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName

const SelectSeparator = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Separator>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
  <SelectPrimitive.Separator
    ref={ref}
    className={cn("-mx-1 my-1 h-px bg-surface-50-900-token", className)}
    {...props}
  />
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName

export {
  Select,
  SelectGroup,
  SelectValue,
  SelectTrigger,
  SelectContent,
  SelectLabel,
  SelectItem,
  SelectSeparator,
}

multiwebinc avatar May 25 '23 19:05 multiwebinc

Has this been resolved yet? I'm also trying to deal with it by enclosing SelectPrimitive.Viewport.

      <ScrollArea
        className={cn(
          position === 'popper' &&
            'w-full min-w-[var(--radix-select-trigger-width)]'
        )}
      >
        <SelectPrimitive.Viewport className="max-h-[40vh] p-1" asChild>
          {children}
        </SelectPrimitive.Viewport>
      </ScrollArea>

kkkkoki avatar Jun 20 '23 20:06 kkkkoki

For me, the solution was to add the proper CSS variable as max height for Content, and add the ScrollUp and ScrollDown buttons around the Viewport.

(classes removed for readability)

<SelectPrimitive.Content className="max-h-[--radix-select-content-available-height]">
  <SelectPrimitive.ScrollUpButton>
    <IconChevronUp />
  </SelectPrimitive.ScrollUpButton>

  <SelectPrimitive.Viewport>
    <SelectPrimitive.Group>{children}</SelectPrimitive.Group>
  </SelectPrimitive.Viewport>

  <SelectPrimitive.ScrollDownButton>
    <IconChevronDown />
  </SelectPrimitive.ScrollDownButton>
</SelectPrimitive.Content>

piotrkulpinski avatar Jun 21 '23 07:06 piotrkulpinski

It's easier to understand this way! I wanted to hide the button, so this is what it looks like! Thank you very much!

<SelectPrimitive.Content  className="max-h-[--radix-select-content-available-height]">
  <SelectPrimitive.ScrollUpButton />

  <SelectPrimitive.Viewport>
    <SelectPrimitive.Group>{children}</SelectPrimitive.Group>
  </SelectPrimitive.Viewport>

  <SelectPrimitive.ScrollDownButton />
</SelectPrimitive.Content>

kkkkoki avatar Jun 21 '23 07:06 kkkkoki

You can add the following Tailwind class if position is set to popper to make it not exceed the viewport height:

<Select.Content className="max-h-[--radix-select-content-available-height]" />

Best solution for now! Cheers!

FelipeJz avatar Jun 23 '23 00:06 FelipeJz

Not sure if helps, but i managed to solve for myself fixing the height and specifying overflow-y: scroll;

<SelectContent
      className="overflow-y-scroll !h-[300px]"
    >
// content
</SelectContent>

joe-ashwell avatar Jun 28 '23 00:06 joe-ashwell

Is there gonna be an official fix for this? I don't know why It hasn't been done yet especially considering the fact the default select from radix doesn't have this issue.

clicktodev avatar Jul 29 '23 13:07 clicktodev

You can add the following Tailwind class if position is set to popper to make it not exceed the viewport height:

<Select.Content className="max-h-[--radix-select-content-available-height]" />

Best solution for now! Cheers!

Cleanest solution by far

shug0 avatar Oct 03 '23 15:10 shug0

For me, I added the below css class and it works across all my components using it:

  .SelectContent {
    width: var(--radix-select-trigger-width);
    max-height: var(--radix-select-content-available-height);
  }

yousihy avatar Oct 03 '23 16:10 yousihy

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 Jul 08 '24 23:07 shadcn