Files
bot/apps/builder/src/components/DropdownList.tsx

133 lines
3.5 KiB
TypeScript
Raw Normal View History

2022-01-15 17:30:20 +01:00
import {
Button,
ButtonProps,
chakra,
FormControl,
FormHelperText,
FormLabel,
HStack,
2022-01-15 17:30:20 +01:00
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
2022-01-15 17:30:20 +01:00
Stack,
} from '@chakra-ui/react'
import { ChevronLeftIcon } from '@/components/icons'
import React, { ReactNode } from 'react'
import { MoreInfoTooltip } from './MoreInfoTooltip'
2022-01-15 17:30:20 +01:00
type Item =
| string
| number
| {
label: string
value: string
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Props<T extends Item> = {
currentItem: string | number | undefined
onItemSelect: (
value: T extends string ? T : T extends number ? T : string,
item?: T
) => void
items: readonly T[]
placeholder?: string
label?: string
isRequired?: boolean
direction?: 'row' | 'column'
helperText?: ReactNode
moreInfoTooltip?: string
2022-01-15 17:30:20 +01:00
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const DropdownList = <T extends Item>({
2022-01-15 17:30:20 +01:00
currentItem,
onItemSelect,
items,
placeholder,
label,
isRequired,
direction = 'column',
helperText,
moreInfoTooltip,
2022-01-15 17:30:20 +01:00
...props
}: Props<T> & ButtonProps) => {
const handleMenuItemClick = (item: T) => () => {
if (typeof item === 'string' || typeof item === 'number')
onItemSelect(item as T extends string ? T : T extends number ? T : string)
else
onItemSelect(
item.value as T extends string ? T : T extends number ? T : string,
item
)
2022-01-15 17:30:20 +01:00
}
return (
<FormControl
isRequired={isRequired}
as={direction === 'column' ? Stack : HStack}
justifyContent="space-between"
2024-01-03 16:29:41 +01:00
width={props.width === 'full' || label ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
<FormLabel display="flex" flexShrink={0} gap="1" mb="0" mr="0">
{label}{' '}
{moreInfoTooltip && (
<MoreInfoTooltip>{moreInfoTooltip}</MoreInfoTooltip>
)}
</FormLabel>
)}
<Menu isLazy>
<MenuButton
as={Button}
rightIcon={<ChevronLeftIcon transform={'rotate(-90deg)'} />}
colorScheme="gray"
justifyContent="space-between"
textAlign="left"
w="full"
{...props}
>
<chakra.span noOfLines={1} display="block">
{currentItem
? getItemLabel(
items?.find((item) =>
typeof item === 'string' || typeof item === 'number'
? currentItem === item
: currentItem === item.value
)
)
: placeholder ?? 'Select an item'}
</chakra.span>
</MenuButton>
<Portal>
<MenuList maxW="500px" zIndex={1500}>
<Stack maxH={'35vh'} overflowY="auto" spacing="0">
{items.map((item) => (
<MenuItem
key={item as unknown as string}
maxW="500px"
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
onClick={handleMenuItemClick(item)}
>
{typeof item === 'object' ? item.label : item}
</MenuItem>
))}
</Stack>
</MenuList>
</Portal>
</Menu>
{helperText && <FormHelperText>{helperText}</FormHelperText>}
</FormControl>
2022-01-15 17:30:20 +01:00
)
}
const getItemLabel = (item?: Item) => {
if (!item) return ''
if (typeof item === 'object') return item.label
return item
}