useProximityHover
trovecn/use-proximity-hoverFreeHook
Tracks cursor distance across a list of items to preview the nearest one before it's clicked.
Install it
npx shadcn@latest add https://trovecn.dev/r/use-proximity-hover.jsonSource
1"use client";23import {4 useRef,5 useState,6 useCallback,7 useEffect,8 useLayoutEffect,9 type Dispatch,10 type RefObject,11 type SetStateAction,12} from "react";1314export interface ItemRect {15 top: number;16 height: number;17 left: number;18 width: number;19}2021/**22 * Shared visual for every proximity-hover consumer's transient "nearest23 * item" wash (Accordion, Tabs, DocsSidebar, DocsMobileSidebar, the Drawer24 * examples' nav/settings lists — anywhere `useProximityHover` drives a25 * pill). A faint --foreground tint reused as-is everywhere.26 */27export const proximityHoverWashClassName = "bg-hover";2829/**30 * Cap on that wash's peak layer-opacity while animating in. At 1 (full31 * layer-opacity) the already-translucent wash lands close enough to a32 * persistent selected/expanded state built from a similarly light neutral33 * token (bg-card, bg-muted, bg-accent/20) to read as the same color,34 * especially in dark mode. 0.4 keeps it a clearly subordinate preview.35 */36export const proximityHoverWashOpacity = 0.4;3738interface UseProximityHoverOptions {39 /**40 * Which direction to resolve the nearest item along.41 * "y" — vertical lists (default): closest by top/height42 * "x" — horizontal strips: closest by left/width43 * "xy" — 2-D grids: closest card across both rows AND columns,44 * measured by Euclidean distance to each item's center45 */46 axis?: "x" | "y" | "xy";47}4849interface UseProximityHoverReturn {50 activeIndex: number | null;51 setActiveIndex: Dispatch<SetStateAction<number | null>>;52 itemRects: ItemRect[];53 /**54 * True once every registered item has been measured and no remeasure is55 * pending, i.e. `itemRects` describes the current item set. Gate absolutely56 * positioned overlays on it: an overlay that mounts against a rect a later57 * pass still corrects animates from the wrong place to the right one, which58 * reads as the highlight sliding in from another row.59 */60 isMeasured: boolean;61 sessionRef: RefObject<number>;62 handlers: {63 onMouseMove: (e: React.MouseEvent) => void;64 onMouseEnter: () => void;65 onMouseLeave: () => void;66 };67 registerItem: (index: number, element: HTMLElement | null) => void;68 /**69 * Invalidates the published rects and runs the hook's coalesced measurement70 * pass again, holding `isMeasured` false until it settles. Reach for it when71 * something other than item registration invalidates layout — a popup that72// … truncated
Files it writes
More from trovecn
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useMergeSplituse-merge-split | trovecn | Hooks | Free | no deps |
