Focus

Focus blocks of code. Dim the unfocused code. Ensure the focused code is visible when there's overflow.

Useful when you want to change a codeblock focus depending on the user's interaction.

content.md
```js
function lorem(ipsum, dolor = 1) {
const sit = ipsum == null ? 0 : ipsum.sit
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
function ipsum(ipsum, dolor = 1) {
return dolor
}
// !focus(1:5)
function dolor(ipsum, dolor = 1) {
const sit = ipsum == null ? 0 : ipsum.sit
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
```
function lorem(ipsum, dolor = 1) {
const sit = ipsum == null ? 0 : ipsum.sit
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
function ipsum(ipsum, dolor = 1) {
return dolor
}
function dolor(ipsum, dolor = 1) {
const sit = ipsum == null ? 0 : ipsum.sit
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
You can also change the focus annotations on a rendered codeblock:

Implementation

We need two things:

  • Set a data-focus={true} to the focused lines
  • Get a ref of the pre element, and scroll it if needed
focus.tsx
"use client"
import {
AnnotationHandler,
InnerPre,
getPreRef,
InnerLine,
} from "codehike/code"
import React, { useLayoutEffect, useRef } from "react"
export const focus: AnnotationHandler = {
name: "focus",
onlyIfAnnotated: true,
PreWithRef: (props) => {
const ref = getPreRef(props)
useScrollToFocus(ref)
return <InnerPre merge={props} />
},
Line: (props) => (
<InnerLine
merge={props}
className=""
/>
),
AnnotatedLine: ({ annotation, ...props }) => (
<InnerLine merge={props} data-focus={true} className="" />
),
}
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
}
})
}