186 lines
5.5 KiB
JavaScript
186 lines
5.5 KiB
JavaScript
import PropTypes from 'prop-types';
|
|
import ExpandLess from '@mui/icons-material/ExpandLess';
|
|
import ExpandMore from '@mui/icons-material/ExpandMore';
|
|
import Box from '@mui/material/Box';
|
|
import Button from '@mui/material/Button';
|
|
import Collapse from '@mui/material/Collapse';
|
|
import List from '@mui/material/List';
|
|
import ListItemButton from '@mui/material/ListItemButton';
|
|
import ListItemText from '@mui/material/ListItemText';
|
|
import Paper from '@mui/material/Paper';
|
|
import Typography from '@mui/material/Typography';
|
|
import throttle from 'lodash/throttle';
|
|
import * as React from 'react';
|
|
import { FixedSizeList } from 'react-window';
|
|
import SearchInput from 'components/SearchInput';
|
|
import useFormatMessage from 'hooks/useFormatMessage';
|
|
import SuggestionItem from './SuggestionItem';
|
|
|
|
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 getPartialArray = (array, length = array.length) => {
|
|
return array.slice(0, length);
|
|
};
|
|
|
|
const renderItemFactory =
|
|
({ onSuggestionClick }) =>
|
|
(props) => (
|
|
<SuggestionItem {...props} onSuggestionClick={onSuggestionClick} />
|
|
);
|
|
|
|
const Suggestions = (props) => {
|
|
const formatMessage = useFormatMessage();
|
|
const { data, onSuggestionClick = () => null } = props;
|
|
const [current, setCurrent] = React.useState(0);
|
|
const [listLength, setListLength] = React.useState(SHORT_LIST_LENGTH);
|
|
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({
|
|
onSuggestionClick,
|
|
}),
|
|
[onSuggestionClick],
|
|
);
|
|
|
|
const expandList = () => {
|
|
setListLength(Infinity);
|
|
};
|
|
|
|
const collapseList = () => {
|
|
setListLength(SHORT_LIST_LENGTH);
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
setListLength(SHORT_LIST_LENGTH);
|
|
}, [current]);
|
|
|
|
const onSearchChange = React.useMemo(
|
|
() =>
|
|
throttle((event) => {
|
|
const search = event.target.value.toLowerCase();
|
|
if (!search) {
|
|
setFilteredData(data);
|
|
return;
|
|
}
|
|
const newFilteredData = data
|
|
.map((stepWithOutput) => {
|
|
return {
|
|
id: stepWithOutput.id,
|
|
name: stepWithOutput.name,
|
|
output: stepWithOutput.output.filter((option) =>
|
|
`${option.label}\n${option.sampleValue}`
|
|
.toLowerCase()
|
|
.includes(search.toLowerCase()),
|
|
),
|
|
};
|
|
})
|
|
.filter((stepWithOutput) => stepWithOutput.output.length);
|
|
setFilteredData(newFilteredData);
|
|
}, 400),
|
|
[data],
|
|
);
|
|
|
|
return (
|
|
<Paper elevation={0} sx={{ width: '100%' }}>
|
|
<Box px={2} pb={2}>
|
|
<SearchInput onChange={onSearchChange} />
|
|
</Box>
|
|
|
|
{filteredData.length > 0 && (
|
|
<List disablePadding>
|
|
{filteredData.map((option, index) => (
|
|
<React.Fragment key={`${index}-${option.name}`}>
|
|
<ListItemButton
|
|
divider
|
|
onClick={() =>
|
|
setCurrent((currentIndex) =>
|
|
currentIndex === index ? null : index,
|
|
)
|
|
}
|
|
sx={{ py: 0.5 }}
|
|
>
|
|
<ListItemText primary={option.name} />
|
|
|
|
{!!option.output?.length &&
|
|
(current === index ? <ExpandLess /> : <ExpandMore />)}
|
|
</ListItemButton>
|
|
|
|
<Collapse
|
|
in={current === index || filteredData.length === 1}
|
|
timeout="auto"
|
|
unmountOnExit
|
|
>
|
|
<FixedSizeList
|
|
height={computeListHeight(
|
|
getPartialArray(option.output || [], listLength).length,
|
|
)}
|
|
width="100%"
|
|
itemSize={LIST_ITEM_HEIGHT}
|
|
itemCount={
|
|
getPartialArray(option.output || [], listLength).length
|
|
}
|
|
overscanCount={2}
|
|
itemData={getPartialArray(option.output || [], listLength)}
|
|
data-test="power-input-suggestion-group"
|
|
>
|
|
{renderItem}
|
|
</FixedSizeList>
|
|
|
|
{(option.output?.length || 0) > listLength && (
|
|
<Button fullWidth onClick={expandList}>
|
|
Show all
|
|
</Button>
|
|
)}
|
|
|
|
{listLength === Infinity && (
|
|
<Button fullWidth onClick={collapseList}>
|
|
Show less
|
|
</Button>
|
|
)}
|
|
</Collapse>
|
|
</React.Fragment>
|
|
))}
|
|
</List>
|
|
)}
|
|
|
|
{filteredData.length === 0 && (
|
|
<Typography sx={{ p: (theme) => theme.spacing(0, 0, 2, 2) }}>
|
|
{formatMessage('powerInputSuggestions.noOptions')}
|
|
</Typography>
|
|
)}
|
|
</Paper>
|
|
);
|
|
};
|
|
|
|
Suggestions.propTypes = {
|
|
data: PropTypes.arrayOf(
|
|
PropTypes.shape({
|
|
id: PropTypes.string.isRequired,
|
|
name: PropTypes.string.isRequired,
|
|
output: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
}),
|
|
).isRequired,
|
|
onSuggestionClick: PropTypes.func,
|
|
};
|
|
|
|
export default Suggestions;
|