Highlight
optics/highlightFreeComponent
Highlight effect component for tabs and other interactive elements.
See it running
Open the demo on optics
optics.agusmayol.com.ar/components/highlightInstall it
npx shadcn@latest add https://optics.agusmayol.com.ar/r/highlight.jsonSource
1"use client";2import * as React from "react";3import { AnimatePresence, motion } from "motion/react";45import { cn } from "@/registry/optics/lib/utils";67const HighlightContext = React.createContext(8 // eslint-disable-next-line @typescript-eslint/no-explicit-any9 undefined,10);1112function useHighlight() {13 const context = React.useContext(HighlightContext);14 if (!context) {15 throw new Error("useHighlight must be used within a HighlightProvider");16 }17 return context;18}1920function Highlight({ ref, ...props }) {21 const {22 as: Component = "div",23 children,24 value,25 defaultValue,26 onValueChange,27 className,28 style,29 transition = { type: "spring", stiffness: 350, damping: 35 },30 hover = false,31 click = true,32 enabled = true,33 controlledItems,34 disabled = false,35 exitDelay = 200,36 mode = "children",37 } = props;3839 const localRef = React.useRef(null);40 React.useImperativeHandle(ref, () => localRef.current);4142 const [activeValue, setActiveValue] = React.useState(43 value ?? defaultValue ?? null,44 );45 const [boundsState, setBoundsState] = React.useState(null);46 const [activeClassNameState, setActiveClassNameState] = React.useState("");4748 const safeSetActiveValue = React.useCallback(49 (id) => {50 setActiveValue((prev) => (prev === id ? prev : id));51 if (id !== activeValue) onValueChange?.(id);52 },53 [activeValue, onValueChange],54 );5556 const safeSetBounds = React.useCallback(57 (bounds) => {58 if (!localRef.current) return;5960 const boundsOffset = props?.boundsOffset ?? {61 top: 0,62 left: 0,63 width: 0,64 height: 0,65 };6667 const containerRect = localRef.current.getBoundingClientRect();68 const newBounds = {69 top: bounds.top - containerRect.top + (boundsOffset.top ?? 0),70 left: bounds.left - containerRect.left + (boundsOffset.left ?? 0),71 width: bounds.width + (boundsOffset.width ?? 0),72 height: bounds.height + (boundsOffset.height ?? 0),73 };7475 setBoundsState((prev) => {76 if (77 prev &&78 prev.top === newBounds.top &&79 prev.left === newBounds.left &&80 prev.width === newBounds.width &&81 prev.height === newBounds.height82 ) {83 return prev;84 }85 return newBounds;86 });87 },88 [props],89 );9091 const clearBounds = React.useCallback(() => {92 setBoundsState((prev) => (prev === null ? prev : null));93 }, []);9495 React.useEffect(() => {96 if (value !== undefined) setActiveValue(value);97 else if (defaultValue !== undefined) setActiveValue(defaultValue);98 }, [value, defaultValue]);99100// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from optics
All 79 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | optics | Components | Free | 5 deps | |
| Alertalert | optics | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | optics | Components | Free | 4 deps | |
| Aspect Ratioaspect-ratio | optics | Components | Free | 2 deps | |
| Auto Heightauto-height | optics | Components | Free | 3 deps | |
| Avataravatar | optics | Components | Free | 3 deps | |
| Badgebadge | optics | Components | Free | 4 deps | |
| Breadcrumbbreadcrumb | optics | Components | Free | 4 deps |
