Collapse

Fold and unfold blocks of code.

content.md
```js
// !collapse(1:4)
function lorem(ipsum, dolor = 1) {
const sit = ipsum == null ? 0 : 1
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
// !collapse(1:4) collapsed
function ipsum(ipsum, dolor = 1) {
const sit = ipsum == null ? 0 : 1
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
```
const sit = ipsum == null ? 0 : 1
dolor = sit - amet(dolor)
return sit ? consectetur(ipsum) : []
}
}

Click on the function signature to collapse/expand the content of the function

Implementation

We use the Collapsible component from shadcn/ui:

npx shadcn-ui@latest add collapsible

For each !collapse annotation we need to add two extra annotations, one for the trigger and one for the content.

code.tsx
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
import { ChevronDownIcon } from "lucide-react"
const collapse: AnnotationHandler = {
name: "collapse",
transform: (annotation: BlockAnnotation) => {
const { fromLineNumber } = annotation
return [
annotation,
{
...annotation,
fromLineNumber: fromLineNumber,
toLineNumber: fromLineNumber,
name: "CollapseTrigger",
},
{
...annotation,
fromLineNumber: fromLineNumber + 1,
name: "CollapseContent",
},
]
},
Block: ({ annotation, children }) => {
return (
<Collapsible defaultOpen={annotation.query !== "collapsed"}>
{children}
</Collapsible>
)
},
}
const collapseTrigger: AnnotationHandler = {
name: "CollapseTrigger",
onlyIfAnnotated: true,
AnnotatedLine: ({ annotation, ...props }) => {
const icon = (
<ChevronDownIcon
className=""
size={15}
/>
)
return (
<CollapsibleTrigger className="">
<InnerLine merge={props} data={{ icon }} />
</CollapsibleTrigger>
)
},
Line: (props) => {
const { data } = props
const icon = data?.icon as React.ReactNode
return (
<div className="">
<span className="">{icon}</span>
<div className="">
<InnerLine merge={props} />
</div>
</div>
)
},
}
const collapseContent: AnnotationHandler = {
name: "CollapseContent",
Block: CollapsibleContent,
}

Then we can pass the collapse, collapseTrigger, and collapseContent handlers to the Pre component:

code.tsx
async function Code({ codeblock }: { codeblock: RawCode }) {
const highlighted = await highlight(codeblock, "github-dark")
return (
<Pre
code={highlighted}
handlers={[collapse, collapseTrigger, collapseContent]}
/>
)
}