diff --git a/package.json b/package.json index 46b8c3f..f525eb8 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@jupyterlab/services": "^7.0.0", "@jupyterlab/ui-components": "^4.4.10", "@lumino/datagrid": "^2.5.3", + "@lumino/polling": "^2.1.5", "@lumino/widgets": "^2.7.1", "apache-arrow": "^21.1.0" }, diff --git a/src/widget.ts b/src/widget.ts index 503483e..15d2ddf 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -8,15 +8,60 @@ import { DataGrid, TextRenderer, } from "@lumino/datagrid"; +import { ConflatableMessage, MessageLoop } from "@lumino/messaging"; +import { Debouncer } from "@lumino/polling"; import { Panel } from "@lumino/widgets"; import type { DocumentRegistry, IDocumentWidget } from "@jupyterlab/docregistry"; import type * as DataGridModule from "@lumino/datagrid"; +import type { ISignal } from "@lumino/signaling"; +import type { ScrollBar } from "@lumino/widgets"; import { FileType } from "./file-types"; import { ArrowModel } from "./model"; import { createToolbar } from "./toolbar"; import type { FileInfo, FileReadOptions } from "./file-options"; +/* grid: DataGrid instance */ + +function installDebouncedScrollBarHook(grid: DataGrid, delay = 100) { + // Access the internal vertical scrollbar and its thumbMoved signal + // biome-ignore lint/suspicious/noExplicitAny: Hacking into private property + const vScrollBar = (grid as any)._vScrollBar as ScrollBar; + // biome-ignore lint/suspicious/noExplicitAny: Hacking into private property + const thumbMoved = (vScrollBar as any).thumbMoved as ISignal; + + // Get the original handler method from the grid + // biome-ignore lint/suspicious/noExplicitAny: Hacking into private property + const originalHandler = (grid as any)._onThumbMoved as (sender: ScrollBar) => void; + + // Disconnect the original handler + thumbMoved.disconnect(originalHandler, grid); + + // Create a debouncer that posts the scroll request after the delay + // The debouncer ensures the last event is always processed + const debouncer = new Debouncer(() => { + MessageLoop.postMessage(grid.viewport, new ConflatableMessage("scroll-request")); + }, delay); + + // Handler that invokes the debouncer on each thumb move + const debouncedHandler = () => { + void debouncer.invoke(); + }; + + // Connect our debounced handler + thumbMoved.connect(debouncedHandler); + + // Return cleanup function + return () => { + // Disconnect our handler + thumbMoved.disconnect(debouncedHandler); + // Reconnect the original handler + thumbMoved.connect(originalHandler, grid); + // Dispose the debouncer + debouncer.dispose(); + }; +} + export namespace ArrowGridViewer { export interface Options { path: string; @@ -51,6 +96,9 @@ export class ArrowGridViewer extends Panel { }; this.addWidget(this._grid); + + installDebouncedScrollBarHook(this._grid, 100); + this._ready = this.initialize(); } diff --git a/yarn.lock b/yarn.lock index f56c549..f83b251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2846,6 +2846,17 @@ __metadata: languageName: node linkType: hard +"@lumino/polling@npm:^2.1.5": + version: 2.1.5 + resolution: "@lumino/polling@npm:2.1.5" + dependencies: + "@lumino/coreutils": ^2.2.2 + "@lumino/disposable": ^2.1.5 + "@lumino/signaling": ^2.1.5 + checksum: 2b510ef4a5ac05470f01281112d1c467ea95f9f783f702d61fe512d8efecda93f360c907eb3e9fd180f507afe79face1d0ca7878a9d844a3e1f588aba7c5a28e + languageName: node + linkType: hard + "@lumino/properties@npm:^2.0.3, @lumino/properties@npm:^2.0.4": version: 2.0.4 resolution: "@lumino/properties@npm:2.0.4" @@ -3698,6 +3709,7 @@ __metadata: "@jupyterlab/testutils": ^4.0.0 "@jupyterlab/ui-components": ^4.4.10 "@lumino/datagrid": ^2.5.3 + "@lumino/polling": ^2.1.5 "@lumino/widgets": ^2.7.1 "@types/jest": ^29.2.0 "@types/json-schema": ^7.0.11