|
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