| 713 | onScrollRef.current?.(false, s); |
| 714 | } |
| 715 | function start(dir_0: -1 | 1): void { |
| 716 | // Record BEFORE early-return: the empty-accumulator reset in check() |
| 717 | // may have zeroed this during the pre-crossing phase (accumulators |
| 718 | // empty until the anchor row enters the capture range). Re-record |
| 719 | // on every call so the corruption is instantly healed. |
| 720 | lastScrolledDirRef.current = dir_0; |
| 721 | if (dirRef.current === dir_0) return; // already going this way |
| 722 | stop(); |
| 723 | dirRef.current = dir_0; |
| 724 | ticksRef.current = 0; |
| 725 | tick(); |
| 726 | // tick() may have hit a scroll boundary and called stop() (dir reset to |
| 727 | // 0). Only start the interval if we're still going — otherwise the |
| 728 | // interval would run forever with dir === 0 doing nothing useful. |
| 729 | if (dirRef.current === dir_0) { |
| 730 | timerRef.current = setInterval(tick, AUTOSCROLL_INTERVAL_MS); |
| 731 | } |
| 732 | } |
| 733 | |
| 734 | // Re-evaluated on every selection change (start/drag/finish/clear). |
| 735 | // Drives drag-to-scroll autoscroll when the drag leaves the viewport. |