mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-01-18 18:55:54 +08:00
Use virtualized list
This commit is contained in:
parent
99f147df66
commit
f31f248937
@ -56,6 +56,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/react-window": "1.8.5",
|
||||||
"redux-devtools": "3.7.0"
|
"redux-devtools": "3.7.0"
|
||||||
},
|
},
|
||||||
"homepage": "/[[.RootPath]]"
|
"homepage": "/[[.RootPath]]"
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Autocomplete from "@material-ui/lab/Autocomplete";
|
import Autocomplete from "@material-ui/lab/Autocomplete";
|
||||||
|
import useMediaQuery from "@material-ui/core/useMediaQuery";
|
||||||
|
import ListSubheader from "@material-ui/core/ListSubheader";
|
||||||
|
import { VariableSizeList, ListChildComponentProps } from "react-window";
|
||||||
import { GroupInfo } from "../api";
|
import { GroupInfo } from "../api";
|
||||||
import { isDarkTheme } from "../theme";
|
import { isDarkTheme } from "../theme";
|
||||||
|
|
||||||
@ -34,6 +37,12 @@ export default function GroupSelect(props: Props) {
|
|||||||
return (
|
return (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
id="task-group-selector"
|
id="task-group-selector"
|
||||||
|
disableListWrap
|
||||||
|
ListboxComponent={
|
||||||
|
ListboxComponent as React.ComponentType<
|
||||||
|
React.HTMLAttributes<HTMLElement>
|
||||||
|
>
|
||||||
|
}
|
||||||
options={props.groups}
|
options={props.groups}
|
||||||
getOptionLabel={(option: GroupInfo) => option.group}
|
getOptionLabel={(option: GroupInfo) => option.group}
|
||||||
style={{ width: 300 }}
|
style={{ width: 300 }}
|
||||||
@ -49,3 +58,83 @@ export default function GroupSelect(props: Props) {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Virtualized list.
|
||||||
|
// Reference: https://v4.mui.com/components/autocomplete/#virtualization
|
||||||
|
|
||||||
|
const LISTBOX_PADDING = 8; // px
|
||||||
|
|
||||||
|
function renderRow(props: ListChildComponentProps) {
|
||||||
|
const { data, index, style } = props;
|
||||||
|
return React.cloneElement(data[index], {
|
||||||
|
style: {
|
||||||
|
...style,
|
||||||
|
top: (style.top as number) + LISTBOX_PADDING,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const OuterElementContext = React.createContext({});
|
||||||
|
|
||||||
|
const OuterElementType = React.forwardRef<HTMLDivElement>((props, ref) => {
|
||||||
|
const outerProps = React.useContext(OuterElementContext);
|
||||||
|
return <div ref={ref} {...props} {...outerProps} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
function useResetCache(data: any) {
|
||||||
|
const ref = React.useRef<VariableSizeList>(null);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (ref.current != null) {
|
||||||
|
ref.current.resetAfterIndex(0, true);
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter for react-window
|
||||||
|
const ListboxComponent = React.forwardRef<HTMLDivElement>(
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
const gridRef = useResetCache(itemCount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
|
<OuterElementContext.Provider value={other}>
|
||||||
|
<VariableSizeList
|
||||||
|
itemData={itemData}
|
||||||
|
height={getHeight() + 2 * LISTBOX_PADDING}
|
||||||
|
width="100%"
|
||||||
|
ref={gridRef}
|
||||||
|
outerElementType={OuterElementType}
|
||||||
|
innerElementType="ul"
|
||||||
|
itemSize={(index) => getChildSize(itemData[index])}
|
||||||
|
overscanCount={5}
|
||||||
|
itemCount={itemCount}
|
||||||
|
>
|
||||||
|
{renderRow}
|
||||||
|
</VariableSizeList>
|
||||||
|
</OuterElementContext.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
@ -2036,6 +2036,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-window@1.8.5":
|
||||||
|
version "1.8.5"
|
||||||
|
resolved "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1"
|
||||||
|
integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^17.0.29":
|
"@types/react@*", "@types/react@^17.0.29":
|
||||||
version "17.0.29"
|
version "17.0.29"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.29.tgz#9535f3fc01a4981ce9cadcf0daa2593c0c2f2251"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.29.tgz#9535f3fc01a4981ce9cadcf0daa2593c0c2f2251"
|
||||||
|
Loading…
Reference in New Issue
Block a user