|
1 | | -import React, { useMemo } from "react" |
| 1 | +import React, { useRef, useMemo } from "react" |
2 | 2 | import styled from "styled-components" |
3 | 3 | import ReactSelect, { components as defaultComponents } from "react-select" |
4 | 4 | import Creatable from "react-select/creatable" |
5 | 5 | import { Icon } from "@/components/icon" |
| 6 | +import { useVirtualizer } from "@tanstack/react-virtual" |
6 | 7 |
|
7 | 8 | const useDataAttrs = (props, name) => { |
8 | 9 | const { "data-ga": dataGA, "data-testid": dataTestId } = props.selectProps |
@@ -83,6 +84,57 @@ const customComponents = { |
83 | 84 | ValueContainer: withDataAttrs(defaultComponents.ValueContainer, "ValueContainer"), |
84 | 85 | } |
85 | 86 |
|
| 87 | +const VirtualizedMenuList = props => { |
| 88 | + const parentRef = useRef() |
| 89 | + |
| 90 | + const virtualizer = useVirtualizer({ |
| 91 | + count: props.options.length, |
| 92 | + getScrollElement: () => parentRef.current, |
| 93 | + estimateSize: () => 20, |
| 94 | + overscan: 5, |
| 95 | + }) |
| 96 | + |
| 97 | + const virtualItems = virtualizer.getVirtualItems() |
| 98 | + |
| 99 | + return ( |
| 100 | + <customComponents.MenuList |
| 101 | + {...props} |
| 102 | + innerRef={parentRef} |
| 103 | + styles={{ |
| 104 | + height: "300px", |
| 105 | + overflow: "auto", |
| 106 | + position: "relative", |
| 107 | + }} |
| 108 | + > |
| 109 | + <div |
| 110 | + style={{ |
| 111 | + height: `${virtualizer.getTotalSize()}px`, |
| 112 | + position: "relative", |
| 113 | + }} |
| 114 | + > |
| 115 | + {virtualItems.map(virtualRow => { |
| 116 | + return ( |
| 117 | + <div |
| 118 | + key={virtualRow.key} |
| 119 | + style={{ |
| 120 | + transform: `translateY(${virtualRow.start}px)`, |
| 121 | + top: 0, |
| 122 | + left: 0, |
| 123 | + right: 0, |
| 124 | + position: "absolute", |
| 125 | + }} |
| 126 | + data-index={virtualRow.index} |
| 127 | + ref={virtualizer.measureElement} |
| 128 | + > |
| 129 | + {props.children[virtualRow.index]} |
| 130 | + </div> |
| 131 | + ) |
| 132 | + })} |
| 133 | + </div> |
| 134 | + </customComponents.MenuList> |
| 135 | + ) |
| 136 | +} |
| 137 | + |
86 | 138 | const makeCustomTheme = theme => selectTheme => { |
87 | 139 | return { |
88 | 140 | ...selectTheme, |
@@ -215,7 +267,7 @@ const makeCustomStyles = (theme, { minWidth, size, ...providedStyles } = {}) => |
215 | 267 |
|
216 | 268 | const getAttrs = props => ({ |
217 | 269 | ...props, |
218 | | - components: { ...customComponents, ...props.components }, |
| 270 | + components: { ...customComponents, MenuList: VirtualizedMenuList, ...props.components }, |
219 | 271 | theme: makeCustomTheme(props.theme), |
220 | 272 | styles: makeCustomStyles(props.theme, props.styles), |
221 | 273 | }) |
|
0 commit comments