|
import PropTypes from 'prop-types'; |
|
import ListItemButton from '@mui/material/ListItemButton'; |
|
import ListItemText from '@mui/material/ListItemText'; |
|
import throttle from 'lodash/throttle'; |
|
import * as React from 'react'; |
|
import { FixedSizeList } from 'react-window'; |
|
import { Typography } from '@mui/material'; |
|
import SearchInput from 'components/SearchInput'; |
|
import useFormatMessage from 'hooks/useFormatMessage'; |
|
import { SearchInputWrapper } from './style'; |
|
import { FieldDropdownOptionPropType } from 'propTypes/propTypes'; |
|
|
|
const SHORT_LIST_LENGTH = 4; |
|
const LIST_ITEM_HEIGHT = 64; |
|
|
|
const computeListHeight = (currentLength) => { |
|
const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength); |
|
return LIST_ITEM_HEIGHT * numberOfRenderedItems; |
|
}; |
|
|
|
const renderItemFactory = |
|
({ onOptionClick }) => |
|
(props) => { |
|
const { index, style, data } = props; |
|
const suboption = data[index]; |
|
return ( |
|
<ListItemButton |
|
sx={{ pl: 4 }} |
|
divider |
|
onClick={(event) => onOptionClick(event, suboption)} |
|
data-test="power-input-suggestion-item" |
|
key={index} |
|
style={style} |
|
> |
|
<ListItemText |
|
primary={suboption.label} |
|
primaryTypographyProps={{ |
|
variant: 'subtitle1', |
|
title: 'Property name', |
|
sx: { fontWeight: 700 }, |
|
}} |
|
secondary={suboption.value} |
|
secondaryTypographyProps={{ |
|
variant: 'subtitle2', |
|
title: 'Sample value', |
|
noWrap: true, |
|
}} |
|
/> |
|
</ListItemButton> |
|
); |
|
}; |
|
|
|
const Options = (props) => { |
|
const formatMessage = useFormatMessage(); |
|
const { data, onOptionClick } = props; |
|
const [filteredData, setFilteredData] = React.useState(data); |
|
React.useEffect( |
|
function syncOptions() { |
|
setFilteredData((filteredData) => { |
|
if (filteredData.length === 0 && filteredData.length !== data.length) { |
|
return data; |
|
} |
|
return filteredData; |
|
}); |
|
}, |
|
[data], |
|
); |
|
const renderItem = React.useMemo( |
|
() => |
|
renderItemFactory({ |
|
onOptionClick, |
|
}), |
|
[onOptionClick], |
|
); |
|
const onSearchChange = React.useMemo( |
|
() => |
|
throttle((event) => { |
|
const search = event.target.value.toLowerCase(); |
|
if (!search) { |
|
setFilteredData(data); |
|
return; |
|
} |
|
const newFilteredData = data.filter((option) => |
|
`${option.label}\n${option.value}` |
|
.toLowerCase() |
|
.includes(search.toLowerCase()), |
|
); |
|
setFilteredData(newFilteredData); |
|
}, 400), |
|
[data], |
|
); |
|
return ( |
|
<> |
|
<SearchInputWrapper> |
|
<SearchInput onChange={onSearchChange} /> |
|
</SearchInputWrapper> |
|
|
|
<FixedSizeList |
|
height={computeListHeight(filteredData.length)} |
|
width="100%" |
|
itemSize={LIST_ITEM_HEIGHT} |
|
itemCount={filteredData.length} |
|
overscanCount={2} |
|
itemData={filteredData} |
|
> |
|
{renderItem} |
|
</FixedSizeList> |
|
|
|
{filteredData.length === 0 && ( |
|
<Typography sx={{ p: (theme) => theme.spacing(0, 0, 2, 2) }}> |
|
{formatMessage('customAutocomplete.noOptions')} |
|
</Typography> |
|
)} |
|
</> |
|
); |
|
}; |
|
|
|
Options.propTypes = { |
|
data: PropTypes.arrayOf(FieldDropdownOptionPropType).isRequired, |
|
onOptionClick: PropTypes.func.isRequired, |
|
}; |
|
|
|
export default Options; |
|
|