|
1 | 1 | "use client" |
2 | 2 |
|
3 | | -import { |
4 | | - HighlightedCode, |
5 | | - Pre, |
6 | | - AnnotationHandler, |
7 | | - InnerPre, |
8 | | - getPreRef, |
9 | | - InnerLine, |
10 | | -} from "codehike/code" |
11 | | -import React, { useLayoutEffect, useRef, useState } from "react" |
| 3 | +import { HighlightedCode, Pre } from "codehike/code" |
| 4 | +import React, { useState } from "react" |
| 5 | +import { focus } from "./focus" |
12 | 6 |
|
13 | 7 | const ranges = { |
14 | 8 | lorem: { fromLineNumber: 1, toLineNumber: 5 }, |
@@ -42,67 +36,18 @@ export function CodeContainer({ code }: { code: HighlightedCode }) { |
42 | 36 | <button |
43 | 37 | onClick={() => setFocused("lorem")} |
44 | 38 | disabled={focused === "lorem"} |
45 | | - className="border border-current rounded px-2 disabled:opacity-60" |
| 39 | + className="border border-current rounded px-2" |
46 | 40 | > |
47 | 41 | focus `lorem` |
48 | 42 | </button>{" "} |
49 | 43 | <button |
50 | 44 | onClick={() => setFocused("dolor")} |
51 | 45 | disabled={focused === "dolor"} |
52 | | - className="border border-current rounded px-2 disabled:opacity-60" |
| 46 | + className="border border-current rounded px-2" |
53 | 47 | > |
54 | 48 | focus `dolor` |
55 | 49 | </button> |
56 | 50 | </div> |
57 | 51 | </> |
58 | 52 | ) |
59 | 53 | } |
60 | | - |
61 | | -const focus: AnnotationHandler = { |
62 | | - name: "focus", |
63 | | - PreWithRef: (props) => { |
64 | | - const ref = getPreRef(props) |
65 | | - useScrollToFocus(ref) |
66 | | - return <InnerPre merge={props} /> |
67 | | - }, |
68 | | - Line: (props) => ( |
69 | | - <InnerLine |
70 | | - merge={props} |
71 | | - className="opacity-50 data-[focus]:opacity-100 px-2" |
72 | | - /> |
73 | | - ), |
74 | | - AnnotatedLine: ({ annotation, ...props }) => ( |
75 | | - <InnerLine merge={props} data-focus={true} className="bg-zinc-700/30" /> |
76 | | - ), |
77 | | -} |
78 | | - |
79 | | -function useScrollToFocus(ref: React.RefObject<HTMLPreElement>) { |
80 | | - const firstRender = useRef(true) |
81 | | - useLayoutEffect(() => { |
82 | | - if (ref.current) { |
83 | | - // find all descendants whith data-focus="true" |
84 | | - const focusedElements = ref.current.querySelectorAll( |
85 | | - "[data-focus=true]", |
86 | | - ) as NodeListOf<HTMLElement> |
87 | | - |
88 | | - // find top and bottom of the focused elements |
89 | | - const containerRect = ref.current.getBoundingClientRect() |
90 | | - let top = Infinity |
91 | | - let bottom = -Infinity |
92 | | - focusedElements.forEach((el) => { |
93 | | - const rect = el.getBoundingClientRect() |
94 | | - top = Math.min(top, rect.top - containerRect.top) |
95 | | - bottom = Math.max(bottom, rect.bottom - containerRect.top) |
96 | | - }) |
97 | | - |
98 | | - // scroll to the focused elements if any part of them is not visible |
99 | | - if (bottom > containerRect.height || top < 0) { |
100 | | - ref.current.scrollTo({ |
101 | | - top: ref.current.scrollTop + top - 10, |
102 | | - behavior: firstRender.current ? "instant" : "smooth", |
103 | | - }) |
104 | | - } |
105 | | - firstRender.current = false |
106 | | - } |
107 | | - }) |
108 | | -} |
0 commit comments