37 lines
1.6 KiB
TypeScript
37 lines
1.6 KiB
TypeScript
import { Ace } from "ace-builds";
|
|
|
|
/**
|
|
* Works around an Ace editor bug where scrolling after a stationary single
|
|
* click selects text instead of scrolling (fleetdm/fleet#48490).
|
|
*
|
|
* Ace's mouse handler keeps an internal "select" capture alive after a
|
|
* mousedown and only tears it down on the next mousemove/mouseup. On a click
|
|
* with no pointer movement (common with trackpads and trackball mice), that
|
|
* capture can stay active. Because Ace's self-healing guard only runs on
|
|
* mousemove, scrolling the editor afterwards keeps re-running its selection
|
|
* logic against the now stationary pointer and extends the selection instead of
|
|
* just scrolling.
|
|
*
|
|
* This releases the stuck capture on wheel events when no mouse button is
|
|
* actually pressed, mirroring Ace's own mousemove guard. The listener uses the
|
|
* capture phase so it runs before Ace processes the scroll, and it lives on
|
|
* editor.container, which is removed when the editor unmounts.
|
|
*/
|
|
// eslint-disable-next-line import/prefer-default-export
|
|
export const releaseStuckSelectionOnScroll = (editor: Ace.Editor): void => {
|
|
// $mouseHandler is not part of Ace's public typings.
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const mouseHandler = (editor as any).$mouseHandler;
|
|
editor.container.addEventListener(
|
|
"wheel",
|
|
(e: WheelEvent) => {
|
|
if (mouseHandler?.isMousePressed && !e.buttons) {
|
|
mouseHandler.releaseMouse?.();
|
|
}
|
|
},
|
|
// Capture phase so this runs before Ace processes the scroll; passive since
|
|
// we never call preventDefault (avoids a non-passive wheel-listener warning).
|
|
{ capture: true, passive: true }
|
|
);
|
|
};
|