@gravity-ui/graph
Version:
Modern graph editor component
151 lines (150 loc) • 7.04 kB
TypeScript
export type TMouseWheelBehavior = "zoom" | "scroll";
/** Explicit wheel input device; `"auto"` uses gesture-shape heuristics. */
export type TWheelInputDevice = "auto" | "mouse" | "trackpad";
/**
* Wheel input classification for camera routing.
*
* The library resolves **intent** (pan vs zoom), not device type. This boundary prepares
* for a future input event bus where raw DOM events (`wheel`, `pointerdown`, …) are
* normalized into semantic graph events (`camera:pan`, `camera:zoom`, …) before Camera
* handles them.
*/
/** Wheel input intent for camera routing (pan vs zoom). */
export declare enum EWheelIntent {
Pan = "pan",
Zoom = "zoom"
}
/** Debug rule ids for {@link createWheelIntentResolver} (see `docs/system/wheel-intent.md`). */
export declare const WHEEL_INTENT_RULE: {
readonly I1_PINCH: "I1:pinch";
readonly I2_HORIZONTAL_OR_DIAGONAL: "I2:horizontal-or-diagonal";
readonly I3_INPUT_DEVICE_TRACKPAD: "I3:input-device-trackpad";
readonly I3_INTEGER_TRACKPAD: "I3:integer-trackpad";
readonly I3_INTEGER_TRACKPAD_SLOW: "I3:integer-trackpad-slow";
readonly I3_RAPID_SMALL: "I3:rapid-small";
readonly I4_MOUSE_WHEEL_STEP: "I4:mouse-wheel-step";
readonly I4_LARGE_STEP: "I4:large-step";
readonly I4_FRACTIONAL_MOUSE: "I4:fractional-mouse";
readonly I4_BURST_SMOOTHING: "I4-burst:smoothing";
readonly I4_INPUT_DEVICE_MOUSE: "I4:input-device-mouse";
readonly I5_LAST_INTENT: "I5:last-intent";
readonly I5_STICKY_STREAM: "I5:sticky-stream";
};
export type TWheelIntentRule = (typeof WHEEL_INTENT_RULE)[keyof typeof WHEEL_INTENT_RULE];
/** Returns true when the rule id belongs to trackpad classification (I3). */
export declare function isI3WheelIntentRule(rule: TWheelIntentRule): boolean;
/** Returns true when the rule id belongs to mouse-wheel classification (I4). */
export declare function isI4WheelIntentRule(rule: TWheelIntentRule): boolean;
/**
* Camera wheel policy passed to {@link TResolveWheelIntent} (from graph constants).
*/
export type TResolveWheelIntentOptions = {
mouseWheelBehavior: TMouseWheelBehavior;
wheelInputDevice?: TWheelInputDevice;
};
/**
* Classifies a wheel event as pan or zoom intent.
* Configured as `resolveWheelIntent` on graph settings (`TGraphSettingsConfig`).
*/
export type TResolveWheelIntent = (event: WheelEvent, options: TResolveWheelIntentOptions) => EWheelIntent;
/** Snapshot of resolver inputs, derived signals, session state, and the winning rule. */
export type TWheelIntentDebugEntry = {
/** {@link TResolveWheelIntentOptions.mouseWheelBehavior}. */
mouseWheelBehavior: TMouseWheelBehavior;
/** {@link TResolveWheelIntentOptions.wheelInputDevice} (defaults to `"auto"`). */
inputDevice: TWheelInputDevice;
/** Raw {@link WheelEvent} fields passed into the resolver. */
input: {
deltaX: number;
deltaY: number;
deltaMode: number;
deltaModeLabel: string;
ctrlKey: boolean;
metaKey: boolean;
shiftKey: boolean;
altKey: boolean;
};
/** Pixel-equivalent deltas after {@link normalizeWheelDelta}. */
normalized: {
deltaX: number;
deltaY: number;
/** min(|x|, |y|) / max(|x|, |y|); null when both axes are ~0. */
diagonalAxisRatio: number | null;
};
/** Timing and resolver session state at classification time. */
session: {
timeSinceLastMs: number;
isRapidStream: boolean;
isInMouseWheelBurst: boolean;
mouseWheelBurstRemainingMs: number | null;
lastIntentBefore: EWheelIntent;
};
/** Boolean predicates evaluated for I1–I5 (see `docs/system/wheel-intent.md`). */
signals: {
isPinchZoom: boolean;
isDiagonalScroll: boolean;
isPredominantHorizontalScroll: boolean;
isClassicMouseWheelStep: boolean;
isDominantAxisLargeWheel: boolean;
isVerticalOnly: boolean;
hasFractionalDelta: boolean;
isSmallDelta: boolean;
/** Trackpads always emit `deltaMode === DOM_DELTA_PIXEL` (0); mice use LINE/PAGE or PIXEL. */
isPixelDeltaMode: boolean;
/** Deprecated `wheelDelta(Y)` ≈ ±120 on Chromium mechanical mouse wheels (Mac Chrome 3× ratio excluded). */
hasLegacyMouseWheelDelta: boolean;
};
/** Winning rule id and resolved intent. */
rule: TWheelIntentRule;
result: EWheelIntent;
};
export type TWheelIntentDebugLogger = (entry: TWheelIntentDebugEntry) => void;
/**
* Enables per-event debug logging for {@link createWheelIntentResolver}.
*
* Stored on `globalThis` so it works even when webpack loads duplicate module copies
* (e.g. Storybook preview vs story bundle).
*
* @example
* ```typescript
* import { enableWheelIntentDebug } from "@gravity-ui/graph";
* enableWheelIntentDebug(); // default console.log: summary + JSON string
* enableWheelIntentDebug(entry => myTelemetry.record(entry)); // custom logger
* enableWheelIntentDebug(null); // disable
* ```
*/
export declare function enableWheelIntentDebug(logger?: TWheelIntentDebugLogger | null): void;
/**
* Returns true when a trackpad modifier-zoom gesture is active (PIXEL mode + ctrl/meta).
* Used by Camera for {@link PINCH_ZOOM_SPEED}; mechanical wheels (LINE/PAGE) are excluded.
*/
export declare function isPinchZoomGesture(event: WheelEvent): boolean;
/**
* Creates the default wheel intent resolver (`TGraphSettingsConfig.resolveWheelIntent`).
*
* Classifies **intent** from gesture shape — not from inferred device type:
*
* | Signal | Intent |
* |---------------------------------------|-----------------|
* | ctrlKey / metaKey + PIXEL scroll | Zoom (I1) |
* | Horizontal or diagonal movement | Pan (I2) |
* | Integer PIXEL delta (trackpad, auto) | Pan (I3) |
* | wheelInputDevice `"trackpad"` | Pan (I3:input-device-trackpad) |
* | wheelInputDevice `"mouse"` | Zoom/Pan (I4:* per behavior) |
* | Large isolated integer PIXEL step | Zoom/Pan (I4)* |
* | Classic mouse wheel step (fractional) | Zoom/Pan (I4)* |
* | Rapid stream + small delta (fractional)| Pan (I3) |
* | Anything else | Last intent (I5)|
* | Rapid stream + confident-rule flip | Sticky prior (I5:sticky-stream) |
*
* *I4 respects `mouseWheelBehavior`: `"scroll"` → Pan, `"zoom"` → Zoom.
*
* Trackpad: small integer PIXEL ticks, or large integer PIXEL inside a rapid stream → pan (I3).
* Isolated large integer PIXEL (Chromium mouse on Windows) → I4 per `mouseWheelBehavior`.
* LINE/PAGE mode (`deltaMode !== 0`) is never trackpad — always mouse (I4).
* See `docs/system/wheel-intent.md` for rationale.
*
* Pass `wheelInputDevice` at resolve time (camera constant `WHEEL_INPUT_DEVICE`) when the app
* knows the primary wheel device and Mac Chrome/YaBrowser heuristics are ambiguous.
*/
export declare function createWheelIntentResolver(): TResolveWheelIntent;