Skip to content

Commit cd02794

Browse files
committed
Split focus annotation example
1 parent f9781e0 commit cd02794

File tree

4 files changed

+80
-74
lines changed

4 files changed

+80
-74
lines changed

apps/web/content/docs/code/focus.mdx

+17-14
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,14 @@ We need two things:
2020
- Get a `ref` of the `pre` element, and scroll it if needed
2121

2222
```tsx focus.tsx -c
23-
"use client"
24-
25-
import {
26-
AnnotationHandler,
27-
InnerPre,
28-
getPreRef,
29-
InnerLine,
30-
} from "codehike/code"
31-
import React, { useLayoutEffect, useRef } from "react"
3223
// !fold[/className="(.*?)"/gm]
24+
import { AnnotationHandler, InnerLine } from "codehike/code"
25+
import { PreWithFocus } from "./focus.client"
3326

3427
export const focus: AnnotationHandler = {
3528
name: "focus",
3629
onlyIfAnnotated: true,
37-
PreWithRef: (props) => {
38-
const ref = getPreRef(props)
39-
useScrollToFocus(ref)
40-
return <InnerPre merge={props} />
41-
},
30+
PreWithRef: PreWithFocus,
4231
Line: (props) => (
4332
<InnerLine
4433
merge={props}
@@ -49,6 +38,20 @@ export const focus: AnnotationHandler = {
4938
<InnerLine merge={props} data-focus={true} className="bg-zinc-700/30" />
5039
),
5140
}
41+
```
42+
43+
```tsx focus.client.tsx -c
44+
// !fold[/className="(.*?)"/gm]
45+
"use client"
46+
47+
import React, { useLayoutEffect, useRef } from "react"
48+
import { AnnotationHandler, InnerPre, getPreRef } from "codehike/code"
49+
50+
export const PreWithFocus: AnnotationHandler["PreWithRef"] = (props) => {
51+
const ref = getPreRef(props)
52+
useScrollToFocus(ref)
53+
return <InnerPre merge={props} />
54+
}
5255

5356
function useScrollToFocus(ref: React.RefObject<HTMLPreElement>) {
5457
const firstRender = useRef(true)

apps/web/demos/focus/code.tsx

+5-60
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
"use client"
22

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"
126

137
const ranges = {
148
lorem: { fromLineNumber: 1, toLineNumber: 5 },
@@ -42,67 +36,18 @@ export function CodeContainer({ code }: { code: HighlightedCode }) {
4236
<button
4337
onClick={() => setFocused("lorem")}
4438
disabled={focused === "lorem"}
45-
className="border border-current rounded px-2 disabled:opacity-60"
39+
className="border border-current rounded px-2"
4640
>
4741
focus `lorem`
4842
</button>{" "}
4943
<button
5044
onClick={() => setFocused("dolor")}
5145
disabled={focused === "dolor"}
52-
className="border border-current rounded px-2 disabled:opacity-60"
46+
className="border border-current rounded px-2"
5347
>
5448
focus `dolor`
5549
</button>
5650
</div>
5751
</>
5852
)
5953
}
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-
}

apps/web/demos/focus/focus.client.tsx

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"use client"
2+
3+
import React, { useLayoutEffect, useRef } from "react"
4+
import { AnnotationHandler, InnerPre, getPreRef } from "codehike/code"
5+
6+
export const PreWithFocus: AnnotationHandler["PreWithRef"] = (props) => {
7+
const ref = getPreRef(props)
8+
useScrollToFocus(ref)
9+
return <InnerPre merge={props} />
10+
}
11+
12+
function useScrollToFocus(ref: React.RefObject<HTMLPreElement>) {
13+
const firstRender = useRef(true)
14+
useLayoutEffect(() => {
15+
if (ref.current) {
16+
// find all descendants whith data-focus="true"
17+
const focusedElements = ref.current.querySelectorAll(
18+
"[data-focus=true]",
19+
) as NodeListOf<HTMLElement>
20+
21+
// find top and bottom of the focused elements
22+
const containerRect = ref.current.getBoundingClientRect()
23+
let top = Infinity
24+
let bottom = -Infinity
25+
focusedElements.forEach((el) => {
26+
const rect = el.getBoundingClientRect()
27+
top = Math.min(top, rect.top - containerRect.top)
28+
bottom = Math.max(bottom, rect.bottom - containerRect.top)
29+
})
30+
31+
// scroll to the focused elements if any part of them is not visible
32+
if (bottom > containerRect.height || top < 0) {
33+
ref.current.scrollTo({
34+
top: ref.current.scrollTop + top - 10,
35+
behavior: firstRender.current ? "instant" : "smooth",
36+
})
37+
}
38+
firstRender.current = false
39+
}
40+
})
41+
}

apps/web/demos/focus/focus.tsx

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { AnnotationHandler, InnerLine } from "codehike/code"
2+
import { PreWithFocus } from "./focus.client"
3+
4+
export const focus: AnnotationHandler = {
5+
name: "focus",
6+
onlyIfAnnotated: true,
7+
PreWithRef: PreWithFocus,
8+
Line: (props) => (
9+
<InnerLine
10+
merge={props}
11+
className="opacity-50 data-[focus]:opacity-100 px-2"
12+
/>
13+
),
14+
AnnotatedLine: ({ annotation, ...props }) => (
15+
<InnerLine merge={props} data-focus={true} className="bg-zinc-700/30" />
16+
),
17+
}

0 commit comments

Comments
 (0)