Files
website/components/magicui/highlighter.tsx
2025-09-12 23:20:36 +02:00

89 lines
1.7 KiB
TypeScript

"use client";
import { useEffect, useRef } from "react";
import { useInView } from "motion/react";
import { annotate } from "rough-notation";
import type React from "react";
type AnnotationAction =
| "highlight"
| "underline"
| "box"
| "circle"
| "strike-through"
| "crossed-off"
| "bracket";
interface HighlighterProps {
children: React.ReactNode;
action?: AnnotationAction;
color?: string;
strokeWidth?: number;
animationDuration?: number;
iterations?: number;
padding?: number;
multiline?: boolean;
isView?: boolean;
}
export function Highlighter({
children,
action = "highlight",
color = "#ffd1dc",
strokeWidth = 1.5,
animationDuration = 600,
iterations = 2,
padding = 2,
multiline = true,
isView = false,
}: HighlighterProps) {
const elementRef = useRef<HTMLSpanElement>(null);
const isInView = useInView(elementRef, {
once: true,
margin: "-10%",
});
// If isView is false, always show. If isView is true, wait for inView
const shouldShow = !isView || isInView;
useEffect(() => {
if (!shouldShow) return;
const element = elementRef.current;
if (!element) return;
const annotation = annotate(element, {
type: action,
color,
strokeWidth,
animationDuration,
iterations,
padding,
multiline,
});
annotation.show();
return () => {
if (element) {
annotate(element, { type: action }).remove();
}
};
}, [
shouldShow,
action,
color,
strokeWidth,
animationDuration,
iterations,
padding,
multiline,
]);
return (
<span ref={elementRef} className="relative inline-block bg-transparent">
{children}
</span>
);
}