Utilities
Hooks and helper functions exported from @mav-labs-inc/mavkit-react alongside the components. Most re-export behavior from @mav-labs-inc/core-js/primitives so vanilla, Angular, and React stay in sync.
cx
Join truthy class names with a space.
function cx(...parts: (string | false | null | undefined)[]): stringcx('btn', isActive && 'btn-active', className) // 'btn btn-active custom'useControllableState
Controlled/uncontrolled state helper — the pattern every form-like MavKit component (TextField, Select, InputStepper, …) uses internally to support both value/onChange and defaultValue usage. When controlled is defined the returned state mirrors it and only reports changes via onChange; otherwise it owns the state internally (seeded from defaultValue) and still reports.
function useControllableState<T>(
controlled: T | undefined,
defaultValue: T,
onChange?: (value: T) => void,
): [T, (next: T) => void]const [open, setOpen] = useControllableState(props.open, false, props.onOpenChange)useDeviceInfo
Reports the current device tier and orientation, updating on resize/orientation change. SSR-safe — everything is null on the server and on the client’s first render (to avoid a hydration mismatch), then resolves to the real viewport immediately after mount.
interface UseDeviceInfoResult {
deviceType: 'mobile' | 'tablet' | 'desktop' | null
orientation: 'portrait' | 'landscape' | null
width: number | null
height: number | null
isMobile: boolean
isTablet: boolean
isDesktop: boolean
isPortrait: boolean
isLandscape: boolean
}
function useDeviceInfo(): UseDeviceInfoResultconst { isMobile, orientation } = useDeviceInfo()Focus management
const FOCUSABLE_SELECTOR: string
/** All focusable descendants of `container`, in DOM order. */
function getFocusable(container: HTMLElement): HTMLElement[]
/**
* Keeps Tab/Shift+Tab cycling within `container`. Call from a keydown
* handler scoped to the container; no-ops for any key other than Tab.
*/
function trapFocus(container: HTMLElement, event: KeyboardEvent): voidMotion
/** True when the user has requested reduced motion at the OS level. */
function prefersReducedMotion(): boolean
/**
* Reads a duration off a CSS custom property (e.g. "180ms") and returns
* milliseconds, falling back to `fallbackMs` if unset or unparsable.
*/
function readDurationVar(el: HTMLElement, varName: string, fallbackMs: number): numberOverlay registry
One registry is shared across vanilla, Angular, and React overlays, so opening any of them (a Modal, Dropdown, Menu, …) closes the others.
interface Overlay {
close(): void
}
/** Register `instance` as open, closing every other registered overlay. */
function registerOverlay(instance: Overlay): void
/** Unregister `instance` (call on close and on teardown). */
function unregisterOverlay(instance: Overlay): voidresolveSnap
The drag-release snap resolution BottomSheet uses to decide which snap point (or dismissal) a gesture lands on.
interface ResolveSnapOptions {
/** Ascending px heights, one per snap point (length >= 1). */
heights: number[]
/** Current dragged height, in px. */
current: number
/** Release velocity in px/ms. Positive = moving down (shrinking). Default 0. */
velocity?: number
/** Whether a downward gesture may dismiss (index: null). Default true. */
dismissible?: boolean
/** px/ms above which a flick wins over nearest-snap. Default 0.5. */
velocityThreshold?: number
/** Fraction of the lowest snap height below which a slow drag still dismisses. Default 0.5. */
dismissRatio?: number
}
/** Resolve a drag-release gesture to a snap index, or `null` to dismiss. */
function resolveSnap(options: ResolveSnapOptions): { index: number | null }