| 1 | /** Save the row at the current offset; a virtualizer's cached visible range can lag a fling. */ |
| 2 | export function resolveWorkGroupScrollAnchor(state: { |
| 3 | readonly data: ReadonlyArray<{ readonly id: string }>; |
| 4 | readonly scroll: number; |
| 5 | readonly positionAtIndex: (index: number) => number | undefined; |
| 6 | }) { |
| 7 | if (state.data.length === 0 || !Number.isFinite(state.scroll)) return undefined; |
| 8 | const scrollOffset = Math.max(0, state.scroll); |
| 9 | let low = 0; |
| 10 | let high = state.data.length - 1; |
| 11 | let index = 0; |
| 12 | let rowTop = state.positionAtIndex(0); |
| 13 | if (rowTop === undefined || !Number.isFinite(rowTop)) return undefined; |
| 14 | |
| 15 | while (low <= high) { |
| 16 | const middle = Math.floor((low + high) / 2); |
| 17 | const top = state.positionAtIndex(middle); |
| 18 | if (top === undefined || !Number.isFinite(top)) return undefined; |
| 19 | if (top <= scrollOffset) { |
| 20 | index = middle; |
| 21 | rowTop = top; |
| 22 | low = middle + 1; |
| 23 | } else { |
| 24 | high = middle - 1; |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | return { |
| 29 | rowId: state.data[index]!.id, |
| 30 | offsetWithinRow: Math.max(0, scrollOffset - rowTop), |
| 31 | scrollOffset, |
| 32 | }; |
| 33 | } |