diff --git a/docs/src/pages/components/autocomplete/Virtualize.js b/docs/src/pages/components/autocomplete/Virtualize.js
index 42e36e3e999cf3..8a46534e5b4079 100644
--- a/docs/src/pages/components/autocomplete/Virtualize.js
+++ b/docs/src/pages/components/autocomplete/Virtualize.js
@@ -3,50 +3,71 @@ import PropTypes from 'prop-types';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import useMediaQuery from '@material-ui/core/useMediaQuery';
+import ListSubheader from '@material-ui/core/ListSubheader';
import { useTheme, makeStyles } from '@material-ui/core/styles';
-import { FixedSizeList } from 'react-window';
+import { VariableSizeList } from 'react-window';
+import { Typography } from '@material-ui/core';
+
+const LISTBOX_PADDING = 8; // px
function renderRow(props) {
const { data, index, style } = props;
-
return React.cloneElement(data[index], {
style: {
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- display: 'block',
...style,
+ top: style.top + LISTBOX_PADDING,
},
});
}
+const OuterElementContext = React.createContext({});
+
+const OuterElementType = React.forwardRef((props, ref) => {
+ const outerProps = React.useContext(OuterElementContext);
+ return
;
+});
+
// Adapter for react-window
const ListboxComponent = React.forwardRef(function ListboxComponent(props, ref) {
const { children, ...other } = props;
+ const itemData = React.Children.toArray(children);
const theme = useTheme();
- const smUp = useMediaQuery(theme.breakpoints.up('sm'));
- const itemCount = Array.isArray(children) ? children.length : 0;
+ const smUp = useMediaQuery(theme.breakpoints.up('sm'), { noSsr: true });
+ const itemCount = itemData.length;
const itemSize = smUp ? 36 : 48;
- const outerElementType = React.useMemo(() => {
- return React.forwardRef((props2, ref2) => );
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
+ const getChildSize = child => {
+ if (React.isValidElement(child) && child.type === ListSubheader) {
+ return 48;
+ }
+
+ return itemSize;
+ };
+
+ const getHeight = () => {
+ if (itemCount > 8) {
+ return 8 * itemSize;
+ }
+ return itemData.map(getChildSize).reduce((a, b) => a + b, 0);
+ };
return (
-
- {renderRow}
-
+
+ getChildSize(itemData[index])}
+ overscanCount={5}
+ itemCount={itemCount}
+ >
+ {renderRow}
+
+
);
});
@@ -75,6 +96,17 @@ const useStyles = makeStyles({
},
});
+const OPTIONS = Array.from(new Array(10000))
+ .map(() => random(10 + Math.ceil(Math.random() * 20)))
+ .sort((a, b) => a.toUpperCase().localeCompare(b.toUpperCase()));
+
+const renderGroup = params => [
+
+ {params.key}
+ ,
+ params.children,
+];
+
export default function Virtualize() {
const classes = useStyles();
@@ -85,10 +117,13 @@ export default function Virtualize() {
disableListWrap
classes={classes}
ListboxComponent={ListboxComponent}
- options={Array.from(new Array(10000)).map(() => random(Math.ceil(Math.random() * 18)))}
+ renderGroup={renderGroup}
+ options={OPTIONS}
+ groupBy={option => option[0].toUpperCase()}
renderInput={params => (
)}
+ renderOption={option => {option}}
/>
);
}
diff --git a/docs/src/pages/components/autocomplete/Virtualize.tsx b/docs/src/pages/components/autocomplete/Virtualize.tsx
index b676d3849a5adc..17cd5e086d46a8 100644
--- a/docs/src/pages/components/autocomplete/Virtualize.tsx
+++ b/docs/src/pages/components/autocomplete/Virtualize.tsx
@@ -1,58 +1,75 @@
import React from 'react';
import TextField from '@material-ui/core/TextField';
-import Autocomplete from '@material-ui/lab/Autocomplete';
+import Autocomplete, { RenderGroupParams } from '@material-ui/lab/Autocomplete';
import useMediaQuery from '@material-ui/core/useMediaQuery';
+import ListSubheader from '@material-ui/core/ListSubheader';
import { useTheme, makeStyles } from '@material-ui/core/styles';
-import { FixedSizeList, ListChildComponentProps } from 'react-window';
+import { VariableSizeList, ListChildComponentProps } from 'react-window';
+import { Typography } from '@material-ui/core';
+
+const LISTBOX_PADDING = 8; // px
function renderRow(props: ListChildComponentProps) {
const { data, index, style } = props;
-
return React.cloneElement(data[index], {
style: {
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- display: 'block',
...style,
+ top: (style.top as number) + LISTBOX_PADDING,
},
});
}
+const OuterElementContext = React.createContext({});
+
+const OuterElementType = React.forwardRef((props, ref) => {
+ const outerProps = React.useContext(OuterElementContext);
+ return ;
+});
+
// Adapter for react-window
-const ListboxComponent = React.forwardRef>(
- function ListboxComponent(props, ref) {
- const { children, ...other } = props;
- const theme = useTheme();
- const smUp = useMediaQuery(theme.breakpoints.up('sm'));
- const itemCount = Array.isArray(children) ? children.length : 0;
- const itemSize = smUp ? 36 : 48;
-
- const outerElementType = React.useMemo(() => {
- return React.forwardRef>(
- (props2, ref2) => ,
- );
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
-
- return (
-
-
(function ListboxComponent(props, ref) {
+ const { children, ...other } = props;
+ const itemData = React.Children.toArray(children);
+ const theme = useTheme();
+ const smUp = useMediaQuery(theme.breakpoints.up('sm'), { noSsr: true });
+ const itemCount = itemData.length;
+ const itemSize = smUp ? 36 : 48;
+
+ const getChildSize = (child: React.ReactNode) => {
+ if (React.isValidElement(child) && child.type === ListSubheader) {
+ return 48;
+ }
+
+ return itemSize;
+ };
+
+ const getHeight = () => {
+ if (itemCount > 8) {
+ return 8 * itemSize;
+ }
+ return itemData.map(getChildSize).reduce((a, b) => a + b, 0);
+ };
+
+ return (
+
+
+ getChildSize(itemData[index])}
overscanCount={5}
itemCount={itemCount}
>
{renderRow}
-
-
- );
- },
-);
+
+
+
+ );
+});
function random(length: number) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@@ -74,6 +91,17 @@ const useStyles = makeStyles({
},
});
+const OPTIONS = Array.from(new Array(10000))
+ .map(() => random(10 + Math.ceil(Math.random() * 20)))
+ .sort((a: string, b: string) => a.toUpperCase().localeCompare(b.toUpperCase()));
+
+const renderGroup = (params: RenderGroupParams) => [
+
+ {params.key}
+ ,
+ params.children,
+];
+
export default function Virtualize() {
const classes = useStyles();
@@ -83,11 +111,14 @@ export default function Virtualize() {
style={{ width: 300 }}
disableListWrap
classes={classes}
- ListboxComponent={ListboxComponent}
- options={Array.from(new Array(10000)).map(() => random(Math.ceil(Math.random() * 18)))}
+ ListboxComponent={ListboxComponent as React.ComponentType>}
+ renderGroup={renderGroup}
+ options={OPTIONS}
+ groupBy={option => option[0].toUpperCase()}
renderInput={params => (
)}
+ renderOption={option => {option}}
/>
);
}