mirror of
https://github.com/dathere/ckanaction.git
synced 2025-12-18 16:29:25 +00:00
103 lines
2.1 KiB
TypeScript
103 lines
2.1 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useRef } from "react"
|
|
import type React from "react"
|
|
import { useInView } from "motion/react"
|
|
import { annotate } from "rough-notation"
|
|
import { type RoughAnnotation } from "rough-notation/lib/model"
|
|
|
|
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 annotationRef = useRef<RoughAnnotation | null>(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 annotationConfig = {
|
|
type: action,
|
|
color,
|
|
strokeWidth,
|
|
animationDuration,
|
|
iterations,
|
|
padding,
|
|
multiline,
|
|
}
|
|
|
|
const annotation = annotate(element, annotationConfig)
|
|
|
|
annotationRef.current = annotation
|
|
annotationRef.current.show()
|
|
|
|
const resizeObserver = new ResizeObserver(() => {
|
|
annotation.hide()
|
|
annotation.show()
|
|
})
|
|
|
|
resizeObserver.observe(element)
|
|
resizeObserver.observe(document.body)
|
|
|
|
return () => {
|
|
if (element) {
|
|
annotate(element, { type: action }).remove()
|
|
resizeObserver.disconnect()
|
|
}
|
|
}
|
|
}, [
|
|
shouldShow,
|
|
action,
|
|
color,
|
|
strokeWidth,
|
|
animationDuration,
|
|
iterations,
|
|
padding,
|
|
multiline,
|
|
])
|
|
|
|
return (
|
|
<span ref={elementRef} className="relative inline-block bg-transparent">
|
|
{children}
|
|
</span>
|
|
)
|
|
}
|