diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index 20fb1116c..0077fd679 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -8023,7 +8023,7 @@ Description: A text highlighter that mimics the effect of a human-drawn marker s --- file: magicui/highlighter.tsx --- "use client" -import { useEffect, useRef } from "react" +import { useLayoutEffect, useRef } from "react" import type React from "react" import { useInView } from "motion/react" import { annotate } from "rough-notation" @@ -8062,7 +8062,6 @@ export function Highlighter({ isView = false, }: HighlighterProps) { const elementRef = useRef(null) - const annotationRef = useRef(null) const isInView = useInView(elementRef, { once: true, @@ -8072,8 +8071,9 @@ export function Highlighter({ // If isView is false, always show. If isView is true, wait for inView const shouldShow = !isView || isInView - useEffect(() => { + useLayoutEffect(() => { const element = elementRef.current + let annotation: RoughAnnotation | null = null let resizeObserver: ResizeObserver | null = null if (shouldShow && element) { @@ -8087,14 +8087,13 @@ export function Highlighter({ multiline, } - const annotation = annotate(element, annotationConfig) - - annotationRef.current = annotation - annotation.show() + const currentAnnotation = annotate(element, annotationConfig) + annotation = currentAnnotation + currentAnnotation.show() resizeObserver = new ResizeObserver(() => { - annotation.hide() - annotation.show() + currentAnnotation.hide() + currentAnnotation.show() }) resizeObserver.observe(element) @@ -8102,10 +8101,7 @@ export function Highlighter({ } return () => { - if (annotationRef.current) { - annotationRef.current.remove() - annotationRef.current = null - } + annotation?.remove() if (resizeObserver) { resizeObserver.disconnect() } @@ -8156,7 +8152,7 @@ export default function HighlighterDemo() { --- file: magicui/highlighter.tsx --- "use client" -import { useEffect, useRef } from "react" +import { useLayoutEffect, useRef } from "react" import type React from "react" import { useInView } from "motion/react" import { annotate } from "rough-notation" @@ -8195,7 +8191,6 @@ export function Highlighter({ isView = false, }: HighlighterProps) { const elementRef = useRef(null) - const annotationRef = useRef(null) const isInView = useInView(elementRef, { once: true, @@ -8205,8 +8200,9 @@ export function Highlighter({ // If isView is false, always show. If isView is true, wait for inView const shouldShow = !isView || isInView - useEffect(() => { + useLayoutEffect(() => { const element = elementRef.current + let annotation: RoughAnnotation | null = null let resizeObserver: ResizeObserver | null = null if (shouldShow && element) { @@ -8220,14 +8216,13 @@ export function Highlighter({ multiline, } - const annotation = annotate(element, annotationConfig) - - annotationRef.current = annotation - annotation.show() + const currentAnnotation = annotate(element, annotationConfig) + annotation = currentAnnotation + currentAnnotation.show() resizeObserver = new ResizeObserver(() => { - annotation.hide() - annotation.show() + currentAnnotation.hide() + currentAnnotation.show() }) resizeObserver.observe(element) @@ -8235,10 +8230,7 @@ export function Highlighter({ } return () => { - if (annotationRef.current) { - annotationRef.current.remove() - annotationRef.current = null - } + annotation?.remove() if (resizeObserver) { resizeObserver.disconnect() } diff --git a/apps/www/public/r/highlighter-demo.json b/apps/www/public/r/highlighter-demo.json index e6e764799..01a22a364 100644 --- a/apps/www/public/r/highlighter-demo.json +++ b/apps/www/public/r/highlighter-demo.json @@ -18,7 +18,7 @@ }, { "path": "registry/magicui/highlighter.tsx", - "content": "\"use client\"\n\nimport { useEffect, useRef } from \"react\"\nimport type React from \"react\"\nimport { useInView } from \"motion/react\"\nimport { annotate } from \"rough-notation\"\nimport { type RoughAnnotation } from \"rough-notation/lib/model\"\n\ntype AnnotationAction =\n | \"highlight\"\n | \"underline\"\n | \"box\"\n | \"circle\"\n | \"strike-through\"\n | \"crossed-off\"\n | \"bracket\"\n\ninterface HighlighterProps {\n children: React.ReactNode\n action?: AnnotationAction\n color?: string\n strokeWidth?: number\n animationDuration?: number\n iterations?: number\n padding?: number\n multiline?: boolean\n isView?: boolean\n}\n\nexport function Highlighter({\n children,\n action = \"highlight\",\n color = \"#ffd1dc\",\n strokeWidth = 1.5,\n animationDuration = 600,\n iterations = 2,\n padding = 2,\n multiline = true,\n isView = false,\n}: HighlighterProps) {\n const elementRef = useRef(null)\n const annotationRef = useRef(null)\n\n const isInView = useInView(elementRef, {\n once: true,\n margin: \"-10%\",\n })\n\n // If isView is false, always show. If isView is true, wait for inView\n const shouldShow = !isView || isInView\n\n useEffect(() => {\n const element = elementRef.current\n let resizeObserver: ResizeObserver | null = null\n\n if (shouldShow && element) {\n const annotationConfig = {\n type: action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n }\n\n const annotation = annotate(element, annotationConfig)\n\n annotationRef.current = annotation\n annotation.show()\n\n resizeObserver = new ResizeObserver(() => {\n annotation.hide()\n annotation.show()\n })\n\n resizeObserver.observe(element)\n resizeObserver.observe(document.body)\n }\n\n return () => {\n if (annotationRef.current) {\n annotationRef.current.remove()\n annotationRef.current = null\n }\n if (resizeObserver) {\n resizeObserver.disconnect()\n }\n }\n }, [\n shouldShow,\n action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n ])\n\n return (\n \n {children}\n \n )\n}\n", + "content": "\"use client\"\n\nimport { useLayoutEffect, useRef } from \"react\"\nimport type React from \"react\"\nimport { useInView } from \"motion/react\"\nimport { annotate } from \"rough-notation\"\nimport { type RoughAnnotation } from \"rough-notation/lib/model\"\n\ntype AnnotationAction =\n | \"highlight\"\n | \"underline\"\n | \"box\"\n | \"circle\"\n | \"strike-through\"\n | \"crossed-off\"\n | \"bracket\"\n\ninterface HighlighterProps {\n children: React.ReactNode\n action?: AnnotationAction\n color?: string\n strokeWidth?: number\n animationDuration?: number\n iterations?: number\n padding?: number\n multiline?: boolean\n isView?: boolean\n}\n\nexport function Highlighter({\n children,\n action = \"highlight\",\n color = \"#ffd1dc\",\n strokeWidth = 1.5,\n animationDuration = 600,\n iterations = 2,\n padding = 2,\n multiline = true,\n isView = false,\n}: HighlighterProps) {\n const elementRef = useRef(null)\n\n const isInView = useInView(elementRef, {\n once: true,\n margin: \"-10%\",\n })\n\n // If isView is false, always show. If isView is true, wait for inView\n const shouldShow = !isView || isInView\n\n useLayoutEffect(() => {\n const element = elementRef.current\n let annotation: RoughAnnotation | null = null\n let resizeObserver: ResizeObserver | null = null\n\n if (shouldShow && element) {\n const annotationConfig = {\n type: action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n }\n\n const currentAnnotation = annotate(element, annotationConfig)\n annotation = currentAnnotation\n currentAnnotation.show()\n\n resizeObserver = new ResizeObserver(() => {\n currentAnnotation.hide()\n currentAnnotation.show()\n })\n\n resizeObserver.observe(element)\n resizeObserver.observe(document.body)\n }\n\n return () => {\n annotation?.remove()\n if (resizeObserver) {\n resizeObserver.disconnect()\n }\n }\n }, [\n shouldShow,\n action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n ])\n\n return (\n \n {children}\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/highlighter.json b/apps/www/public/r/highlighter.json index f8b3ce58d..c6adc0f5b 100644 --- a/apps/www/public/r/highlighter.json +++ b/apps/www/public/r/highlighter.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/highlighter.tsx", - "content": "\"use client\"\n\nimport { useEffect, useRef } from \"react\"\nimport type React from \"react\"\nimport { useInView } from \"motion/react\"\nimport { annotate } from \"rough-notation\"\nimport { type RoughAnnotation } from \"rough-notation/lib/model\"\n\ntype AnnotationAction =\n | \"highlight\"\n | \"underline\"\n | \"box\"\n | \"circle\"\n | \"strike-through\"\n | \"crossed-off\"\n | \"bracket\"\n\ninterface HighlighterProps {\n children: React.ReactNode\n action?: AnnotationAction\n color?: string\n strokeWidth?: number\n animationDuration?: number\n iterations?: number\n padding?: number\n multiline?: boolean\n isView?: boolean\n}\n\nexport function Highlighter({\n children,\n action = \"highlight\",\n color = \"#ffd1dc\",\n strokeWidth = 1.5,\n animationDuration = 600,\n iterations = 2,\n padding = 2,\n multiline = true,\n isView = false,\n}: HighlighterProps) {\n const elementRef = useRef(null)\n const annotationRef = useRef(null)\n\n const isInView = useInView(elementRef, {\n once: true,\n margin: \"-10%\",\n })\n\n // If isView is false, always show. If isView is true, wait for inView\n const shouldShow = !isView || isInView\n\n useEffect(() => {\n const element = elementRef.current\n let resizeObserver: ResizeObserver | null = null\n\n if (shouldShow && element) {\n const annotationConfig = {\n type: action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n }\n\n const annotation = annotate(element, annotationConfig)\n\n annotationRef.current = annotation\n annotation.show()\n\n resizeObserver = new ResizeObserver(() => {\n annotation.hide()\n annotation.show()\n })\n\n resizeObserver.observe(element)\n resizeObserver.observe(document.body)\n }\n\n return () => {\n if (annotationRef.current) {\n annotationRef.current.remove()\n annotationRef.current = null\n }\n if (resizeObserver) {\n resizeObserver.disconnect()\n }\n }\n }, [\n shouldShow,\n action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n ])\n\n return (\n \n {children}\n \n )\n}\n", + "content": "\"use client\"\n\nimport { useLayoutEffect, useRef } from \"react\"\nimport type React from \"react\"\nimport { useInView } from \"motion/react\"\nimport { annotate } from \"rough-notation\"\nimport { type RoughAnnotation } from \"rough-notation/lib/model\"\n\ntype AnnotationAction =\n | \"highlight\"\n | \"underline\"\n | \"box\"\n | \"circle\"\n | \"strike-through\"\n | \"crossed-off\"\n | \"bracket\"\n\ninterface HighlighterProps {\n children: React.ReactNode\n action?: AnnotationAction\n color?: string\n strokeWidth?: number\n animationDuration?: number\n iterations?: number\n padding?: number\n multiline?: boolean\n isView?: boolean\n}\n\nexport function Highlighter({\n children,\n action = \"highlight\",\n color = \"#ffd1dc\",\n strokeWidth = 1.5,\n animationDuration = 600,\n iterations = 2,\n padding = 2,\n multiline = true,\n isView = false,\n}: HighlighterProps) {\n const elementRef = useRef(null)\n\n const isInView = useInView(elementRef, {\n once: true,\n margin: \"-10%\",\n })\n\n // If isView is false, always show. If isView is true, wait for inView\n const shouldShow = !isView || isInView\n\n useLayoutEffect(() => {\n const element = elementRef.current\n let annotation: RoughAnnotation | null = null\n let resizeObserver: ResizeObserver | null = null\n\n if (shouldShow && element) {\n const annotationConfig = {\n type: action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n }\n\n const currentAnnotation = annotate(element, annotationConfig)\n annotation = currentAnnotation\n currentAnnotation.show()\n\n resizeObserver = new ResizeObserver(() => {\n currentAnnotation.hide()\n currentAnnotation.show()\n })\n\n resizeObserver.observe(element)\n resizeObserver.observe(document.body)\n }\n\n return () => {\n annotation?.remove()\n if (resizeObserver) {\n resizeObserver.disconnect()\n }\n }\n }, [\n shouldShow,\n action,\n color,\n strokeWidth,\n animationDuration,\n iterations,\n padding,\n multiline,\n ])\n\n return (\n \n {children}\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/registry/magicui/highlighter.tsx b/apps/www/registry/magicui/highlighter.tsx index 0d19a947f..89a492142 100644 --- a/apps/www/registry/magicui/highlighter.tsx +++ b/apps/www/registry/magicui/highlighter.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useRef } from "react" +import { useLayoutEffect, useRef } from "react" import type React from "react" import { useInView } from "motion/react" import { annotate } from "rough-notation" @@ -39,7 +39,6 @@ export function Highlighter({ isView = false, }: HighlighterProps) { const elementRef = useRef(null) - const annotationRef = useRef(null) const isInView = useInView(elementRef, { once: true, @@ -49,8 +48,9 @@ export function Highlighter({ // If isView is false, always show. If isView is true, wait for inView const shouldShow = !isView || isInView - useEffect(() => { + useLayoutEffect(() => { const element = elementRef.current + let annotation: RoughAnnotation | null = null let resizeObserver: ResizeObserver | null = null if (shouldShow && element) { @@ -64,14 +64,13 @@ export function Highlighter({ multiline, } - const annotation = annotate(element, annotationConfig) - - annotationRef.current = annotation - annotation.show() + const currentAnnotation = annotate(element, annotationConfig) + annotation = currentAnnotation + currentAnnotation.show() resizeObserver = new ResizeObserver(() => { - annotation.hide() - annotation.show() + currentAnnotation.hide() + currentAnnotation.show() }) resizeObserver.observe(element) @@ -79,10 +78,7 @@ export function Highlighter({ } return () => { - if (annotationRef.current) { - annotationRef.current.remove() - annotationRef.current = null - } + annotation?.remove() if (resizeObserver) { resizeObserver.disconnect() }