import type { RefObject } from 'react'; import type { DateTimeInputRef } from './types.js'; import type { SpinButtonHandler } from '../spin-buttons/spin-button-group/types.js'; /** @internal */ export declare function checkIfValueTypeAlignment(textNode: Node): boolean; /** * Endpoints captured by `deferContentEditableRestore` before re-attaching CE. * Safari clips the live selection to the active CE host after CE re-attachment, * so a bubble-phase Backspace/Delete handler can no longer see the full * multi-element range. Consumers read these to feed `planBulkClear`. * @internal */ export type SelectionEndpoints = { anchorNode: Node; anchorOffset: number; focusNode: Node; focusOffset: number; }; /** * Returns and clears the multi-element selection endpoints captured for `root` * by `deferContentEditableRestore` just before the most recent CE re-attach. * Returns `undefined` when no stash exists (no preceding multi-element * selection, or already consumed). * @internal */ export declare function consumePreRestoreEndpoints(root: HTMLElement): SelectionEndpoints | undefined; /** * Strips [contenteditable] to allow cross-element selection, then restores it on the next `eventType`. * `initialAnchorSpinButton` is the spinbutton resolved from bounding-box geometry before CE is stripped — * used when Safari misreports anchorNode as a container element on far-right clicks. * @internal */ export declare function removeContentEditable(rootElement: HTMLElement, removeEventTarget: Document | HTMLElement, eventType: 'mouseup' | 'keyup', customRemovalFn?: (event: Event, hasMultiElementSelection: boolean) => void, initialAnchorSpinButton?: HTMLElement | null): void; export declare function extendSelection(key: 'ArrowLeft' | 'ArrowRight' | 'Home' | 'End'): void; /** * Helper function that allows to expand the selection to the entire range. * @internal */ export declare function extendSelectionToAll(): void; /** * Collapses a multi-element selection (anchorNode !== focusNode) to one of its * boundaries and focuses the spinbutton or expression input at that boundary. * * Mirrors the natural Chrome/Safari behaviour of plain ArrowLeft/ArrowRight on a * non-collapsed selection: ArrowLeft → collapse to start, ArrowRight → collapse * to end. Firefox does not collapse — it shrinks the selection one step at a * time — so without this helper a Shift+Arrow extension followed by plain * arrows would require N presses before the cursor finally moves on Firefox. * * Returns true when a collapse was performed (callers should preventDefault), * false when the selection does not need handling here. * @internal */ export declare function collapseMultiElementSelection(rootElement: HTMLElement, direction: 'left' | 'right'): boolean; /** * One entry in a spin-button undo snapshot. * Uses a stable inputRef + Map key pair instead of a direct handler reference * so that the undo step always resolves the *current* (post-re-render) handler. * @internal */ export type SpinButtonSnapshotEntry = { inputRef: RefObject; key: Intl.DateTimeFormatPartTypes; previousText: string | null; }; /** * Inspects the spin buttons of the supplied inputs and returns a snapshot for * every spin button whose content overlaps the selected range. * * The snapshot captures the current `textContent` of each covered element so * it can later be passed to. * @internal */ export declare function buildSpinButtonSnapshot(anchorNode: Node | null, anchorOffset: number, focusNode: Node | null, focusOffset: number, inputRefs: RefObject[]): SpinButtonSnapshotEntry[]; /** * Restores spin-button values from a previously captured snapshot. * For each entry the current handler is resolved via the stable `inputRef` + * Map key, so stale handler references from before a React re-render are never * used. * @internal */ export declare function restoreSpinButtonSnapshot(snapshot: SpinButtonSnapshotEntry[]): void; /** * After a bulk-clear, determines the expression value to apply for each affected input: * - **0 survivors** (all spin buttons cleared): resets the input to an empty expression (`""`). * - **1 survivor** (exactly one spin button was outside the selection): carries its value * into expression mode via `setExpressionValue` rather than leaving a partial date. * - **2+ survivors** (partial deletion): leaves the remaining spin buttons as-is. * * **Must be called BEFORE the clear loop** so that textContent is read while * the DOM is still intact (TextSpinButton clears its DOM only on next render). * Returns a Map of inputRef → expression text for every input that qualifies. * After clearing, iterate the map and call `inputRef.current.setExpressionValue(text)`. * @internal */ export declare function findExpressionFallbacks(snapshot: SpinButtonSnapshotEntry[]): Map, string>; /** Returns the spin-button handler for the first entry in a snapshot, or `undefined`. * Returns `undefined` when that entry is the first or second spin button in its * group — in those cases callers should fall back to expression-input focus. * @internal */ export declare function getFirstSnapshotHandler(snapshot: SpinButtonSnapshotEntry[]): SpinButtonHandler | null | undefined; /** Returns the spin-button handler for the last entry in a snapshot, or `undefined`. * Returns `undefined` when the last input's cleared count means it switched to * relative mode — callers should fall back to expression-input focus in that case. * @internal */ export declare function getLastSnapshotHandler(snapshot: SpinButtonSnapshotEntry[]): SpinButtonHandler | null | undefined; /** * Returns the first ref in `inputRefs` order that has an expression input to * focus after a bulk clear, considering both directly cleared expression inputs * and inputs that transitioned to expression mode via expressionFallbacks. * Using `inputRefs` order ensures focus lands on the leftmost affected input * even when the selection's anchor is in an already-empty expression input. * @internal */ export declare function findFirstExpressionFocusTarget(inputRefs: RefObject[], affectedExpressionRefs: RefObject[], expressionFallbacks: Map, string>): RefObject | undefined; /** * Returns the expression input element when the selection is non-collapsed but * covers only part of the text within a single expression input. In that case * the caller should handle the deletion explicitly rather than relying on the * bulk-clear path or native browser behaviour. * Returns null when the selection is not a partial single-expression selection. * @internal */ export declare function getPartialExpressionElement(selection: Selection, inputRefs: RefObject[]): HTMLSpanElement | null; /** * Returns expression boundaries touched by the current selection. * - Two different expressions: `[firstExpressionElement, lastExpressionElement]` in document order. * - Mixed boundary (one expression + one non-expression boundary): `[expressionElement, expressionElement]`. * - Otherwise: `null`. * @internal */ export declare function getCrossExpressionElements(selection: Selection, inputRefs: RefObject[]): [HTMLSpanElement, HTMLSpanElement] | null; /** * Focuses an expression element and places the cursor at the given range, * or at the end of the element when no range is provided. * The data-restoring-selection guard tells the focus handler to skip selectAll(). * @internal */ export declare function focusExpression(expressionElement: HTMLSpanElement, range?: Range): void; /** * Deletes the selected range within a single expression input, collapses the * cursor to the deletion point, and fires an input event so the expression * handler can update React state. * @internal */ export declare function deletePartialExpression(selection: Selection, expressionElement: HTMLSpanElement): void; /** * Plans a bulk clear operation by analyzing the current selection * and computing everything needed to clear affected spin buttons and expression inputs. * Must be called before any DOM writes so that textContent is still intact. * @internal */ export declare function planBulkClear(selection: Selection, inputRefs: RefObject[], endpointsOverride?: SelectionEndpoints): { snapshot: SpinButtonSnapshotEntry[]; affectedExpressionRefs: RefObject[]; expressionFallbacks: Map, string>; clearedExpressions: Array<{ inputRef: RefObject; previousText: string; }>; }; /** * Deletes the selected portion across two expression inputs using two separate * single-element ranges. Using one cross-element range with deleteContents() * does not modify text inside contenteditable boundaries; splitting into two * intra-element ranges avoids that limitation entirely. * Focus is placed at the end of whatever text remains in the first expression. * @internal */ export declare function deleteCrossExpressionPartial(selection: Selection, [firstExpressionElement, lastExpressionElement]: [ HTMLSpanElement, HTMLSpanElement ]): void; /** * Returns the keyup callback passed to `removeContentEditable` after a * bulk-clear (cut or delete). Once the key is released and contenteditable * is restored, it decides where focus lands: * - If `firstElement` is `undefined` (the selection started at a leading spin * button), focus the first affected expression input. * - Otherwise keep focus on that spin button. * @internal */ export declare function restoreFocusAfterBulkClear(firstSpinButtonHandler: SpinButtonHandler | null | undefined, focusTarget: RefObject | undefined): void;