diff --git a/optuna_dashboard/ts/components/TrialList.tsx b/optuna_dashboard/ts/components/TrialList.tsx index cfde6536..444fe5af 100644 --- a/optuna_dashboard/ts/components/TrialList.tsx +++ b/optuna_dashboard/ts/components/TrialList.tsx @@ -29,6 +29,8 @@ import { actionCreator } from "../action" import { TrialFormWidgets } from "./TrialFormWidgets" import { TrialArtifactCards } from "./TrialArtifactCards" +import { useVirtualizer } from "@tanstack/react-virtual" + const states: TrialState[] = [ "Complete", "Pruned", @@ -344,6 +346,14 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({ (state) => allTrials.filter((t) => t.state === state).length ) }, [studyDetail?.trials]) + const listParentRef = React.useRef(null) + + const rowVirtualizer = useVirtualizer({ + count: trials.length, + getScrollElement: () => listParentRef.current, + estimateSize: () => 75, + overscan: 10, + }) const trialListWidth = 200 @@ -353,13 +363,14 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({ return ( - + {trials.length} Trials @@ -417,65 +428,91 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({ - {trials.map((trial) => { - return ( - - { - if (e.shiftKey) { - let next: number[] - const selectedNumbers = selected.map((t) => t.number) - const alreadySelected = - selectedNumbers.findIndex((n) => n === trial.number) >= - 0 - if (alreadySelected) { - next = selectedNumbers.filter((n) => n !== trial.number) - } else { - next = [...selectedNumbers, trial.number] - } - navigate( - getTrialListLink(trial.study_id, excludedStates, next) - ) - } else { - navigate( - getTrialListLink(trial.study_id, excludedStates, [ - trial.number, - ]) - ) - } - }} - selected={ - selected.findIndex((t) => t.number === trial.number) !== -1 - } + + {rowVirtualizer.getVirtualItems().map((virtualItem) => { + console.log(virtualItem.index, virtualItem.start, virtualItem.end) + const trial = trials[virtualItem.index] + return ( + - - - - {isBestTrial(trial.trial_id) ? ( + { + if (e.shiftKey) { + let next: number[] + const selectedNumbers = selected.map((t) => t.number) + const alreadySelected = + selectedNumbers.findIndex( + (n) => n === trial.number + ) >= 0 + if (alreadySelected) { + next = selectedNumbers.filter( + (n) => n !== trial.number + ) + } else { + next = [...selectedNumbers, trial.number] + } + navigate( + getTrialListLink(trial.study_id, excludedStates, next) + ) + } else { + navigate( + getTrialListLink(trial.study_id, excludedStates, [ + trial.number, + ]) + ) + } + }} + selected={ + selected.findIndex((t) => t.number === trial.number) !== + -1 + } + sx={{ + display: "flex", + flexDirection: "column", + alignItems: "flex-start", + }} + > + + - ) : null} - - - - ) - })} + {isBestTrial(trial.trial_id) ? ( + + ) : null} + + + + ) + })} + diff --git a/package-lock.json b/package-lock.json index 1d9a286e..11df1cdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@mui/material": "^5.12.1", "@react-three/drei": "^9.80.0", "@react-three/fiber": "^8.13.6", + "@tanstack/react-virtual": "^3.1.2", "@types/three": "^0.154.0", "axios": "^1.6.0", "elkjs": "^0.8.2", @@ -3810,6 +3811,31 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.1.2.tgz", + "integrity": "sha512-qibmxtctgOZo2I+3Rw5GR9kXgaa15U5r3/idDY1ItUKW15UK7GhCfyIfE6qYuJ1fxQF6dJDsD8SbpPyuJgpxuA==", + "dependencies": { + "@tanstack/virtual-core": "3.1.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.1.2.tgz", + "integrity": "sha512-DATZJs8iejkIUqXZe6ruDAnjFo78BKnIIgqQZrc7CmEFqfLEN/TPD91n4hRfo6hpRB6xC00bwKxv7vdjFNEmOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz", @@ -17926,6 +17952,19 @@ "@sinonjs/commons": "^1.7.0" } }, + "@tanstack/react-virtual": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.1.2.tgz", + "integrity": "sha512-qibmxtctgOZo2I+3Rw5GR9kXgaa15U5r3/idDY1ItUKW15UK7GhCfyIfE6qYuJ1fxQF6dJDsD8SbpPyuJgpxuA==", + "requires": { + "@tanstack/virtual-core": "3.1.2" + } + }, + "@tanstack/virtual-core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.1.2.tgz", + "integrity": "sha512-DATZJs8iejkIUqXZe6ruDAnjFo78BKnIIgqQZrc7CmEFqfLEN/TPD91n4hRfo6hpRB6xC00bwKxv7vdjFNEmOg==" + }, "@testing-library/dom": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz", diff --git a/package.json b/package.json index 9e6e59e9..5ab63bdc 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@mui/material": "^5.12.1", "@react-three/drei": "^9.80.0", "@react-three/fiber": "^8.13.6", + "@tanstack/react-virtual": "^3.1.2", "@types/three": "^0.154.0", "axios": "^1.6.0", "elkjs": "^0.8.2",