import type { ReactNode } from 'react'
import React, { useMemo } from 'react'
import type { BaseProps } from '@toptal/picasso-shared'
import { MenuItem } from '@toptal/picasso-menu'
import { Typography } from '@toptal/picasso-typography'
import { NonNativeSelectOption } from '../NonNativeSelectOption'
import { SelectOptions } from '../SelectOptions'
import type {
FocusEventType,
ItemProps,
Option,
OptionGroups,
SelectProps,
Selection,
} from '../SelectBase'
import { flattenOptions, isOptionsType } from '../SelectBase'
// TODO: Replace with a real component as soon as it's implemented
// https://toptal-core.atlassian.net/browse/FX-1479
// Note: In the current implementation children are siblings for the group node.
// If in the new MenuGroup children are inside the group node, that will
// brake the current implementation of highlightedIndex calculations in this
// component and selectedIndex in ScrollMenu component.
interface MenuGroupProps extends BaseProps {
group: string
children: ReactNode
}
const MenuGroup = (props: MenuGroupProps) => {
const { group, children, ...rest } = props
return (
<>
{children}
>
)
}
export type Props = Pick<
SelectProps,
'multiple' | 'noOptionsText' | 'renderOption'
> & {
options: Option[] | OptionGroups
highlightedIndex: number | null
filterOptionsValue: string
getItemProps: (option: Option, index: number) => ItemProps
onBlur?: FocusEventType
fixedHeader?: ReactNode
fixedFooter?: ReactNode
selection: Selection
testIds?: {
noOptions?: string
}
}
const renderOptions = ({
options,
getItemProps,
selection,
highlightedIndex,
offset = 0,
renderOption,
}: Pick<
Props,
'getItemProps' | 'selection' | 'highlightedIndex' | 'renderOption'
> & { options: Option[]; offset?: number }) => {
return options.map((option, index) => {
return (
{renderOption?.(option)}
)
})
}
const renderGroups = ({
groups,
getItemProps,
selection,
highlightedIndex,
renderOption,
}: Pick<
Props,
'getItemProps' | 'selection' | 'highlightedIndex' | 'renderOption'
> & { groups: OptionGroups }) => {
let optionsCount = 0
return Object.keys(groups).map(group => {
const menuGroups = (
{renderOptions({
options: groups[group],
getItemProps,
selection,
highlightedIndex,
offset: optionsCount,
renderOption,
})}
)
optionsCount += groups[group].length
return menuGroups
})
}
const addOffsetToHighlightedIndex = (
groups: OptionGroups,
highlightedIndex: number | null
) => {
if (!highlightedIndex) {
return highlightedIndex
}
let optionsCount = 0
const offset =
Object.values(groups).findIndex(group => {
optionsCount += group.length
const isHighlightedOptionInGroup = highlightedIndex < optionsCount
return isHighlightedOptionInGroup
}) + 1
return highlightedIndex + offset
}
const NonNativeSelectOptions = ({
options,
renderOption = () => null,
highlightedIndex,
getItemProps,
onBlur,
selection,
filterOptionsValue,
noOptionsText,
fixedHeader,
fixedFooter,
testIds,
}: Props) => {
const flatOptions: Option[] = useMemo(
() => flattenOptions(options),
[options]
)
if (!flatOptions.length && filterOptionsValue) {
return (
)
}
return (
{isOptionsType(options)
? renderOptions({
options,
getItemProps,
selection,
highlightedIndex,
renderOption,
})
: renderGroups({
groups: options,
getItemProps,
selection,
highlightedIndex,
renderOption,
})}
)
}
NonNativeSelectOptions.displayName = 'NonNativeSelectOptions'
export default NonNativeSelectOptions