import type { BarPlacement } from '../layout/bar-layout'; import type { SchedulerEvent } from '../data/scheduler.types'; import type { SchedulerModule, SchedulerRuntime } from '../scheduler-runtime'; import type { SlotAxis } from '../time/slot-axis'; import { type BarSource } from './event-selection-service'; /** * Pixels the pointer must travel before a press becomes a drag. * * Matches `DRAG_THRESHOLD` in `drag-drop/drag-drop-engine.ts` on purpose: a user * who has learned how much slack a row drag allows should find the same slack on * an event bar. It is duplicated as a literal rather than imported because that * module keeps it private and importing it would drag the whole drag-drop engine * into the scheduler's module graph for one number. */ export declare const DRAG_THRESHOLD = 4; /** Selector for the resize affordances, which this service must not react to. */ export declare const HANDLE_SELECTOR = ".pg-scheduler-bar__handle"; /** Class of the drag/resize ghost. Mirrors `theme/scheduler-styles.ts`. */ export declare const PREVIEW_CLASS = "pg-scheduler-preview"; /** Modifier applied to the ghost when dropping would be rejected. */ export declare const PREVIEW_INVALID_CLASS = "pg-scheduler-preview--invalid"; /** * Resolves the resource under a viewport Y coordinate. * * Passed in rather than computed here because row hit-testing needs the grid's * render window and its `rowOriginY` rebasing, both of which the plugin already * holds and neither of which belongs in an interaction service. Keeping it a * callback also means the service stays testable with a stub that returns a * fixed id, and never touches `elementFromPoint` -- which would hit the ghost * itself and force a style recalculation on every pointer move. * * @returns The resource id under the pointer, or `null` when the pointer is over * a non-resource row (group header, detail row, summary) or outside the body. */ export type ResolveResourceAt = (clientY: number) => string | null; /** * Whether an event's type forbids interaction. * * Resolved from config rather than from the bar's `--locked` class so a drag * cannot be started against an event whose bar is not currently rendered, and so * the answer does not depend on the renderer having painted yet. The per-type * value wins over the global default, matching how the renderer layers the two. */ export declare function isEventLocked(runtime: SchedulerRuntime, event: SchedulerEvent): boolean; /** * Finds this frame's placement for a pool handle. * * A linear scan over the placements is deliberate: the array is capped at the * layout's `maxBars` (800 by default) and this runs once per gesture start, not * per pointer move. An id-to-placement map would have to be rebuilt every frame * to stay correct, which is a per-frame cost paid for a per-gesture lookup. */ export declare function placementOf(bars: BarSource, handle: number): BarPlacement | undefined; /** * Snaps a time to the boundary of the slot containing it. * * Goes through pixels (`pxAt` then `indexAt` then `timeOf`) rather than dividing * by a slot duration because slots are not equal in general -- months, quarters, * years and any day/week range crossing a DST transition all produce unequal * slots, and dividing would drift. The axis already owns that arithmetic in both * its uniform and prefix forms, so routing through it is correct for every view. */ export declare function snapTime(axis: SlotAxis, t: number): number; /** * Creates the ghost element inside a layer. * * Uses the layer's own document so the scheduler works inside a popped-out * window or an iframe, where the global `document` is the wrong one. */ export declare function createPreview(layerEl: HTMLElement): HTMLElement; /** * Positions the ghost. * * Only geometry is written here -- every colour, border and radius comes from the * `.pg-scheduler-preview` rule and therefore from theme tokens. `transform` is * used for the offset rather than `left`/`top` because it is composited: moving * the ghost 60 times a second then costs no layout, matching how the renderer * positions the bars themselves. */ export declare function positionPreview(el: HTMLElement, left: number, top: number, width: number, height: number, valid: boolean): void; /** * Pointer-driven moving of event bars. * * ## Why the index is suppressed instead of updated during the drag * * The obvious implementation mutates the event on every pointer move and lets * the renderer follow. That re-sorts the resource's columns up to sixty times a * second, invalidates the lane layout on every frame, and leaves the model in a * state the user has not committed to -- so an abort has to be undone. Instead * the dragged event is hidden from queries with * {@link EventIndex.suppress}, a single ghost element tracks the pointer, and * the index is written exactly once on drop. Cancelling is then free: drop the * ghost and clear the suppression. * * ## Why the time delta is not a simple scale of the pixel delta * * On a prefix axis, milliseconds-per-pixel varies from slot to slot (February is * not January), so `dx * msPerPx` drifts. Asking the axis for the time at the * origin and at the origin plus `dx` gives the correct delta on every axis type, * which is why the origin pixel is captured at gesture start. */ export declare class EventDragService implements SchedulerModule { private readonly runtime; private readonly abort; private readonly bars; private readonly layerEl; private readonly resolveResourceAt; private session; private preview; /** Reused between overlap checks so a pointer move allocates nothing. */ private readonly overlapScratch; /** * @param runtime - Shared scheduler state; the index is written on drop. * @param layerEl - Layer holding the bars. Pointer listeners are delegated * here, and the ghost is appended here so it shares the layer's scroll and * row-origin transforms. * @param bars - Renderer accessor, used to read the dragged bar's committed * geometry so the ghost starts exactly on top of it. * @param resolveResourceAt - Row hit-test owned by the plugin. See * {@link ResolveResourceAt}. */ constructor(runtime: SchedulerRuntime, layerEl: HTMLElement, bars: BarSource, resolveResourceAt: ResolveResourceAt); /** Whether a move gesture has passed the threshold and is currently previewing. */ isDragging(): boolean; /** * Aborts any in-flight drag and removes every listener. * * Cancelling first matters: a destroy mid-drag would otherwise leave the * dragged event suppressed and therefore invisible for the rest of the grid's * life. */ destroy(): void; /** * Arms a potential drag. * * Nothing is suppressed and no ghost exists yet -- this only records the * origin, so a press that turns out to be a click costs one object and no DOM * work at all. */ private readonly onPointerDown; /** Promotes the press to a drag once it clears the threshold, then tracks the pointer. */ private readonly onPointerMove; /** Commits the move, or does nothing if the press never became a drag. */ private readonly onPointerUp; /** A cancelled pointer (browser gesture, lost capture) aborts rather than commits. */ private readonly onPointerCancel; /** Escape aborts the drag, leaving the event exactly where it was. */ private readonly onKeyDown; /** * Starts previewing: hides the original, creates the ghost, captures the * pointer. * * Pointer capture is taken here rather than at pointerdown so a simple click * keeps its normal event target -- capturing early would retarget the * subsequent `click` at the layer and break selection. */ private begin; /** Recomputes the proposed position from the pointer and repaints the ghost. */ private track; /** * Overlap validation for the proposed position. * * The dragged event is already suppressed from the index, so it cannot match * itself and no exclusion filter is needed -- which is a second reason * suppression is the right mechanism rather than a convenience. */ private isDropAllowed; /** * Applies the move, subject to the host's veto. * * The order -- emit `beforeMove`, run the veto, mutate, emit `afterMove` -- is * fixed so a listener on the bus sees the same sequence a hook does. When * `managed` is `false` nothing is mutated and `afterMove` is a *request*: the * host persists it and pushes new data back, exactly like the grid's unmanaged * row drag. */ private commit; /** Aborts an in-flight gesture without committing anything. */ private cancel; /** * Tears the gesture down. * * Suppression is cleared unconditionally -- including on the veto and cancel * paths -- because an event left suppressed is invisible with no way for the * user to recover it. */ private finish; } //# sourceMappingURL=event-drag-service.d.ts.map