/**
* Value produced by parsing a data attribute
*/
export type AttributeValue = boolean | null | number | string;
/**
* Physical spring constants (produced by `springParameters`)
*/
export interface SpringParams {
stiffness: number;
damping: number;
mass: number;
}
/**
* Current spring state
*/
export interface SpringState {
position: number;
velocity: number;
}
/**
* Parse data attribute value to appropriate JavaScript type
* @param value - The attribute value to parse
* @returns The parsed value
* @example
* parseAttributeValue('true'); // returns true (boolean)
* parseAttributeValue('false'); // returns false (boolean)
* parseAttributeValue('null'); // returns null
* parseAttributeValue('123'); // returns 123 (number)
* parseAttributeValue('45.67'); // returns 45.67 (number)
* parseAttributeValue('some string'); // returns 'some string' (string)
*/
export declare const parseAttributeValue: (value: string) => AttributeValue;
/**
* Extract data attributes from an element with a given prefix
* @param element - The DOM element to extract data attributes from
* @param prefix - The data attribute prefix (without 'data-')
* @returns Object with camelCase keys and parsed values
* @example
* // Given an element
* extractDataAttributes(element);
* // Returns: { backdrop: true }
*/
export declare const extractDataAttributes: (element: unknown, prefix?: string) => Record;
/**
* Resolve an element from a selector or an element reference
* @param elementOrSelector - Element or CSS selector
* @param context - Context for querySelector
* @returns Resolved element or null
* @example
* resolveElement('#myElement'); // returns the element with ID 'myElement'
* resolveElement(someElement); // returns someElement if it's an Element
*/
export declare const resolveElement: (elementOrSelector: Element | string | null | undefined, context?: Document | Element) => Element | null;
/**
* Clamp a number between a minimum and maximum value
* @param value - The value to clamp
* @param min - The minimum value
* @param max - The maximum value
* @returns The clamped value
* @example
* clamp(5, 1, 10); // returns 5
* clamp(0, 1, 10); // returns 1
* clamp(15, 1, 10); // returns 10
*/
export declare const clamp: (value: number, min: number, max: number) => number;
/**
* Extract target selector from trigger element
* @param element - The trigger element
* @returns Target selector or null
* @example
* // Given an element
* extractTargetSelector(element); // returns '#mySheet'
* // Given an element
* extractTargetSelector(element); // returns '#mySheet'
* // Given an element
* extractTargetSelector(element); // returns '#mySheet'
* // Given an element
* extractTargetSelector(element); // returns null
*/
export declare const extractTargetSelector: (element: unknown) => string | null;
/**
* Calculate scrollbar width by creating a temporary element
* @returns Scrollbar width in pixels
* @example
* const scrollbarWidth = getScrollbarWidth(); // e.g., returns 15
*/
export declare const getScrollbarWidth: () => number;
/**
* Get the type of a value as a lowercase string
* @param value - Any value
* @returns Type name (e.g. 'string', 'number', 'array')
* @example
* getValueType(123); // returns 'number'
* getValueType('hello'); // returns 'string'
* getValueType([1, 2, 3]); // returns 'array'
* getValueType({ key: 'value' }); // returns 'object'
* getValueType(null); // returns 'null'
* getValueType(undefined); // returns 'undefined'
*/
export declare const getValueType: (value: unknown) => string;
/**
* Parse a type string into an array of allowed types
* @param types - Type string like "(string|number)"
* @returns Array of allowed type names
* @example
* parseExpectedTypes('(string|number)'); // returns ['string', 'number']
* parseExpectedTypes('boolean'); // returns ['boolean']
* parseExpectedTypes('(array|object|null)'); // returns ['array', 'object', 'null']
* parseExpectedTypes(''); // returns []
*/
export declare const parseExpectedTypes: (types: string) => string[];
/**
* Validate configuration object against type definitions.
*
* Acts as an assertion function: when it returns without throwing, every
* property listed in `configTypes` is runtime-proven to match its declared
* type, so the config can be treated as `T` from that point on.
*
* @param componentName - Component name for error messages
* @param config - Configuration object to validate
* @param configTypes - Type definitions
* @throws {TypeError} If a config property has an invalid type
* @example
* const config = { backdrop: true };
* const configTypes = { backdrop: 'boolean' };
* validateConfigTypes('Sheet', config, configTypes); // No error
*
* const invalidConfig = { backdrop: 'yes' };
* validateConfigTypes('Sheet', invalidConfig, configTypes);
* // Throws TypeError: [Sheet] Option "backdrop" has invalid type: expected boolean, but received string.
*/
export declare function validateConfigTypes(componentName: string, config?: Record, configTypes?: Record): asserts config is Record & T;
/**
* Get the vertical translation (Y-axis) of a DOM element
* @param element - The DOM element to measure
* @returns The vertical translation in pixels
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix}
* @example
* // Given an element with transform: translateY(100px);
* const translateY = getTranslateY(someElement); // returns 100
*
* // Given an element with no transform
* const translateY = getTranslateY(someElement); // returns 0
*/
export declare const getTranslateY: (element: unknown) => number;
/**
* Apple's rubber band formula (reverse-engineered from UIScrollView).
*
* Attempt to move past a boundary results in diminishing returns:
* the displayed offset asymptotically approaches `dimension` but never reaches it.
* Initial slope equals `coefficient`, so the first few pixels of overscroll
* move at (coefficient × 100)% of finger speed.
*
* Formula: b = (1 - 1 / (x * c / d + 1)) * d
* Equivalent: b = (x * d * c) / (d + c * x)
*
* @param offset - How far past the boundary (must be >= 0)
* @param dimension - Reference dimension (sheet height)
* @param coefficient - Resistance coefficient (Apple uses 0.55)
* @returns Displayed offset (always >= 0, always < dimension)
* @see {@link https://gist.github.com/originell/6961057} Analysis of Apple's rubber band scrolling
*/
export declare const rubberBand: (offset: number, dimension: number, coefficient: number) => number;
/**
* Convert designer-friendly spring parameters to physical constants.
*
* Apple introduced this parameterization at WWDC 2018 ("Designing Fluid Interfaces"):
* - `dampingRatio` controls bounce (1.0 = no bounce, <1.0 = bouncy)
* - `response` controls speed (lower = faster, analogous to duration)
*
* Conversion assumes mass = 1:
* - `stiffness = (2π / response)²`
* - `damping = 4π · dampingRatio / response`
*
* @param dampingRatio - Damping ratio (1.0 = critically damped, 0.8 = slight bounce)
* @param response - Response time in seconds (0.4 is a good default)
* @returns Physical spring constants
*/
export declare const springParameters: (dampingRatio: number, response: number) => SpringParams;
/**
* Check whether a spring animation has settled (converged to target).
*
* The animation is considered settled when both the distance from the target
* and the velocity are below the given threshold. Using 0.5px as the default
* threshold matches Apple's behavior - sub-pixel movements are imperceptible.
*
* @param state - Current spring state
* @param target - Target position
* @param positionThreshold - Maximum distance from target (px)
* @param velocityThreshold - Maximum velocity (px/s)
* @returns True if the spring has settled
*/
export declare const isSpringSettled: (state: SpringState, target: number, positionThreshold?: number, velocityThreshold?: number) => boolean;
/**
* Project how far a decelerating object will travel before stopping.
*
* This is Apple's formula from WWDC 2018 "Designing Fluid Interfaces".
* Given a release velocity and a deceleration rate, it computes the total
* displacement the object would cover if allowed to coast to a stop.
*
* UIScrollView uses two deceleration rates:
* - 0.998 (UIScrollView.DecelerationRate.normal) - default, ~499px per 1000px/s
* - 0.99 (UIScrollView.DecelerationRate.fast) - snappier, ~99px per 1000px/s
*
* The exact integral formula is `-v₀ / (1000 · ln(d))`, which differs from
* Apple's published approximation by less than 1%.
*
* @param velocity - Release velocity in px/s (positive = downward)
* @param decelerationRate - Deceleration rate (0–1, higher = more coasting)
* @returns Projected displacement in px (same sign as velocity)
*/
export declare const projectDisplacement: (velocity: number, decelerationRate: number) => number;
/**
* Advance spring state by dt using the exact analytical solution.
*
* Unconditionally stable for any dt, stiffness, or damping - no sub-stepping
* or numerical integration required. Solves the ODE exactly:
* m·x'' + c·x' + k·(x − target) = 0
*
* Three regimes, determined by the damping ratio ζ = c / (2√km):
* ζ < 1 underdamped - oscillatory exponential decay
* ζ ≈ 1 critically damped - fastest non-oscillatory convergence
* ζ > 1 overdamped - two real exponentials, slower than critical
*
* @param state - Current state
* @param target - Target position the spring pulls toward
* @param params - Spring constants
* @param dt - Time step in seconds (any positive value is stable)
* @returns Exact state at t + dt
*/
export declare const solveSpring: (state: SpringState, target: number, params: SpringParams, dt: number) => SpringState;
/**
* Windowed velocity tracker for touch/pointer gestures.
*
* iOS's UIPanGestureRecognizer computes velocity from recent touch samples
* rather than the full gesture history. This class replicates that approach
* by maintaining a sliding window of {timestamp, position} samples and
* computing velocity from the oldest and newest entries within the window.
*
* Key behaviors:
* - Only samples within the last `windowMs` milliseconds are considered
* - If the gesture pauses (gap > windowMs before release), velocity is 0
* - Guards against division by zero from identical timestamps
* - Uses event.timeStamp (backed by performance.now) for microsecond precision
*/
export declare class VelocityTracker {
#private;
/**
* @param windowMs - Time window for velocity calculation (ms)
*/
constructor(windowMs?: number);
/**
* Record a position sample at the given timestamp.
*
* Old samples (older than 2× the window) are pruned to prevent
* unbounded memory growth during long drag gestures.
*
* @param timestamp - Event timestamp in ms (use event.timeStamp)
* @param position - Current position in px
*/
addSample(timestamp: number, position: number): void;
/**
* Compute velocity from samples within the time window.
*
* Returns 0 if:
* - Fewer than 2 samples exist within the window
* - Time delta between first and last sample is 0 (identical timestamps)
* - The gesture has paused (no recent samples)
*
* @param currentTimestamp - Current time in ms (from pointerup event)
* @returns Velocity in px/ms (positive = downward movement)
*/
getVelocity(currentTimestamp: number): number;
/**
* Clear all recorded samples. Call when a new gesture begins.
*/
reset(): void;
}
//# sourceMappingURL=utils.d.ts.map