-
-
Notifications
You must be signed in to change notification settings - Fork 159
/
Copy pathcode.tsx
108 lines (100 loc) · 3.02 KB
/
code.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"use client"
import {
HighlightedCode,
Pre,
AnnotationHandler,
InnerPre,
getPreRef,
InnerLine,
} from "codehike/code"
import React, { useLayoutEffect, useRef, useState } from "react"
const ranges = {
lorem: { fromLineNumber: 1, toLineNumber: 5 },
ipsum: { fromLineNumber: 7, toLineNumber: 11 },
dolor: { fromLineNumber: 11, toLineNumber: 15 },
}
export function CodeContainer({ code }: { code: HighlightedCode }) {
const [focused, setFocused] = useState<"lorem" | "ipsum" | "dolor">("dolor")
return (
<>
<Pre
className="m-0 px-0 max-h-72 scroll-smooth overflow-auto bg-zinc-950/90"
code={{
...code,
annotations: [
{
name: "focus",
query: "",
...ranges[focused],
},
],
}}
handlers={[focus]}
/>
<div className="p-2 mt-auto font-light text-center text-white">
You can also change the focus annotations on a rendered codeblock:
</div>
<div className="flex justify-center gap-2 pb-4 text-white">
<button
onClick={() => setFocused("lorem")}
disabled={focused === "lorem"}
className="border border-current rounded px-2 disabled:opacity-60"
>
focus `lorem`
</button>{" "}
<button
onClick={() => setFocused("dolor")}
disabled={focused === "dolor"}
className="border border-current rounded px-2 disabled:opacity-60"
>
focus `dolor`
</button>
</div>
</>
)
}
const focus: AnnotationHandler = {
name: "focus",
PreWithRef: (props) => {
const ref = getPreRef(props)
useScrollToFocus(ref)
return <InnerPre merge={props} />
},
Line: (props) => (
<InnerLine
merge={props}
className="opacity-50 data-[focus]:opacity-100 px-2"
/>
),
AnnotatedLine: ({ annotation, ...props }) => (
<InnerLine merge={props} data-focus={true} className="bg-zinc-700/30" />
),
}
function useScrollToFocus(ref: React.RefObject<HTMLPreElement>) {
const firstRender = useRef(true)
useLayoutEffect(() => {
if (ref.current) {
// find all descendants whith data-focus="true"
const focusedElements = ref.current.querySelectorAll(
"[data-focus=true]",
) as NodeListOf<HTMLElement>
// find top and bottom of the focused elements
const containerRect = ref.current.getBoundingClientRect()
let top = Infinity
let bottom = -Infinity
focusedElements.forEach((el) => {
const rect = el.getBoundingClientRect()
top = Math.min(top, rect.top - containerRect.top)
bottom = Math.max(bottom, rect.bottom - containerRect.top)
})
// scroll to the focused elements if any part of them is not visible
if (bottom > containerRect.height || top < 0) {
ref.current.scrollTo({
top: ref.current.scrollTop + top - 10,
behavior: firstRender.current ? "instant" : "smooth",
})
}
firstRender.current = false
}
})
}