焦点

聚焦代码块。使未聚焦的代码变暗。确保在溢出时聚焦的代码可见。

当您想要根据用户的交互改变代码块焦点时很有用。

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

我们需要两样东西:

  • 为聚焦的行设置 data-focus={true}
  • 获取 pre 元素的 ref,并在需要时滚动它
focus.tsx
import { AnnotationHandler, InnerLine } from "codehike/code"
import { PreWithFocus } from "./focus.client"
export const focus: AnnotationHandler = {
name: "focus",
onlyIfAnnotated: true,
PreWithRef: PreWithFocus,
Line: (props) => (
<InnerLine
merge={props}
className=""
/>
),
AnnotatedLine: ({ annotation, ...props }) => (
<InnerLine merge={props} data-focus={true} className="" />
),
}
focus.client.tsx
"use client"
import React, { useLayoutEffect, useRef } from "react"
import { AnnotationHandler, InnerPre, getPreRef } from "codehike/code"
export const PreWithFocus: AnnotationHandler["PreWithRef"] = (props) => {
const ref = getPreRef(props)
useScrollToFocus(ref)
return <InnerPre merge={props} />
}
function useScrollToFocus(ref: React.RefObject<HTMLPreElement>) {
const firstRender = useRef(true)
useLayoutEffect(() => {
if (ref.current) {
// 查找所有具有 data-focus="true" 的后代元素
const focusedElements = ref.current.querySelectorAll(
"[data-focus=true]",
) as NodeListOf<HTMLElement>
// 查找聚焦元素的顶部和底部
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)
})
// 如果聚焦元素的任何部分不可见,则滚动到聚焦元素
if (bottom > containerRect.height || top < 0) {
ref.current.scrollTo({
top: ref.current.scrollTop + top - 10,
behavior: firstRender.current ? "instant" : "smooth",
})
}
firstRender.current = false
}
})
}