docs: make code blocks collapsible (#7606)
* added collapsible code feature * fixed side shadow * fix build errors * change design * make code blocks collapsible
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
export * from "./use-collapsible"
|
||||
export * from "./use-collapsible-code-lines"
|
||||
export * from "./use-copy"
|
||||
export * from "./use-current-learning-path"
|
||||
export * from "./use-is-browser"
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
LineInputProps,
|
||||
LineOutputProps,
|
||||
Token,
|
||||
TokenInputProps,
|
||||
TokenOutputProps,
|
||||
} from "prism-react-renderer"
|
||||
import React, { useCallback, useMemo } from "react"
|
||||
import { CodeBlockCollapsibleLines } from "../../components/CodeBlock/Collapsible/Lines"
|
||||
import { useCollapsible } from "../use-collapsible"
|
||||
|
||||
export type HighlightProps = {
|
||||
getLineProps: (input: LineInputProps) => LineOutputProps
|
||||
getTokenProps: (input: TokenInputProps) => TokenOutputProps
|
||||
}
|
||||
|
||||
export type CollapsibleCodeLines = {
|
||||
collapsibleLinesStr?: string
|
||||
getLines: (
|
||||
token: Token[][],
|
||||
highlightProps: HighlightProps,
|
||||
lineNumberOffset?: number
|
||||
) => React.ReactNode
|
||||
}
|
||||
|
||||
export type CollapsedCodeLinesPosition = "start" | "end"
|
||||
|
||||
export const useCollapsibleCodeLines = ({
|
||||
collapsibleLinesStr,
|
||||
getLines,
|
||||
}: CollapsibleCodeLines) => {
|
||||
const collapsedRange:
|
||||
| {
|
||||
start: number
|
||||
end: number
|
||||
}
|
||||
| undefined = useMemo(() => {
|
||||
if (!collapsibleLinesStr) {
|
||||
return
|
||||
}
|
||||
|
||||
const splitCollapsedLines = collapsibleLinesStr
|
||||
.split("-")
|
||||
.map((lineNumber) => parseInt(lineNumber))
|
||||
|
||||
if (
|
||||
splitCollapsedLines.length !== 2 ||
|
||||
(splitCollapsedLines[0] !== 1 && splitCollapsedLines[1] < 2)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
start: splitCollapsedLines[0],
|
||||
end: splitCollapsedLines[1],
|
||||
}
|
||||
}, [collapsibleLinesStr])
|
||||
|
||||
const type: CollapsedCodeLinesPosition | undefined = useMemo(() => {
|
||||
if (!collapsedRange) {
|
||||
return undefined
|
||||
}
|
||||
return collapsedRange.start === 1 ? "start" : "end"
|
||||
}, [collapsedRange])
|
||||
|
||||
const collapsibleHookResult = useCollapsible({
|
||||
unmountOnExit: false,
|
||||
translateEnabled: false,
|
||||
heightAnimation: true,
|
||||
})
|
||||
|
||||
const getCollapsedLinesElm = useCallback(
|
||||
({
|
||||
tokens,
|
||||
highlightProps,
|
||||
}: {
|
||||
tokens: Token[][]
|
||||
highlightProps: HighlightProps
|
||||
}) => {
|
||||
if (!collapsedRange || !type) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const startIndex =
|
||||
type === "start" ? collapsedRange.start - 1 : collapsedRange.start
|
||||
|
||||
const lines = tokens.slice(
|
||||
startIndex,
|
||||
Math.min(collapsedRange.end, tokens.length)
|
||||
)
|
||||
|
||||
return (
|
||||
<CodeBlockCollapsibleLines {...collapsibleHookResult} type={type}>
|
||||
{getLines(lines, highlightProps, startIndex)}
|
||||
</CodeBlockCollapsibleLines>
|
||||
)
|
||||
},
|
||||
[collapsedRange, collapsibleHookResult]
|
||||
)
|
||||
|
||||
const getNonCollapsedLinesElm = useCallback(
|
||||
({
|
||||
tokens,
|
||||
highlightProps,
|
||||
}: {
|
||||
tokens: Token[][]
|
||||
highlightProps: HighlightProps
|
||||
}) => {
|
||||
if (!collapsedRange) {
|
||||
return getLines(tokens, highlightProps)
|
||||
}
|
||||
|
||||
const isCollapseBeginning = collapsedRange.start === 1
|
||||
const lines = tokens.slice(
|
||||
isCollapseBeginning ? collapsedRange.end : 0,
|
||||
isCollapseBeginning ? undefined : collapsedRange.start
|
||||
)
|
||||
|
||||
return getLines(
|
||||
lines,
|
||||
highlightProps,
|
||||
isCollapseBeginning ? collapsedRange.end : 0
|
||||
)
|
||||
},
|
||||
[collapsedRange, collapsibleHookResult]
|
||||
)
|
||||
|
||||
return {
|
||||
getCollapsedLinesElm,
|
||||
getNonCollapsedLinesElm,
|
||||
type,
|
||||
...collapsibleHookResult,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
"use client"
|
||||
|
||||
import React, { useState } from "react"
|
||||
import { CSSTransition } from "react-transition-group"
|
||||
|
||||
export type CollapsibleProps = {
|
||||
initialValue?: boolean
|
||||
heightAnimation?: boolean
|
||||
translateEnabled?: boolean
|
||||
onClose?: () => void
|
||||
unmountOnExit?: boolean
|
||||
}
|
||||
|
||||
export type CollapsibleReturn = {
|
||||
getCollapsibleElms: (children: React.ReactNode) => React.JSX.Element
|
||||
collapsed: boolean
|
||||
setCollapsed: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
export const useCollapsible = ({
|
||||
initialValue = true,
|
||||
heightAnimation = false,
|
||||
translateEnabled = true,
|
||||
onClose,
|
||||
unmountOnExit = true,
|
||||
}: CollapsibleProps): CollapsibleReturn => {
|
||||
const [collapsed, setCollapsed] = useState(initialValue)
|
||||
|
||||
const getCollapsibleElms = (children: React.ReactNode) => (
|
||||
<CSSTransition
|
||||
unmountOnExit={unmountOnExit}
|
||||
in={!collapsed}
|
||||
timeout={150}
|
||||
onEnter={(node: HTMLElement) => {
|
||||
if (heightAnimation) {
|
||||
node.classList.add("transition-[height]")
|
||||
node.style.height = `0px`
|
||||
} else {
|
||||
node.classList.add("!mb-docs_2", "!mt-0")
|
||||
if (translateEnabled) {
|
||||
node.classList.add("translate-y-docs_1", "transition-transform")
|
||||
}
|
||||
}
|
||||
}}
|
||||
onEntering={(node: HTMLElement) => {
|
||||
if (heightAnimation) {
|
||||
node.style.height = `${node.scrollHeight}px`
|
||||
}
|
||||
}}
|
||||
onEntered={(node: HTMLElement) => {
|
||||
if (heightAnimation) {
|
||||
node.style.height = `auto`
|
||||
}
|
||||
}}
|
||||
onExit={(node: HTMLElement) => {
|
||||
if (heightAnimation) {
|
||||
node.style.height = `${node.scrollHeight}px`
|
||||
} else {
|
||||
if (translateEnabled) {
|
||||
node.classList.add("transition-transform", "!-translate-y-docs_1")
|
||||
}
|
||||
setTimeout(() => {
|
||||
onClose?.()
|
||||
}, 100)
|
||||
}
|
||||
}}
|
||||
onExiting={(node: HTMLElement) => {
|
||||
if (heightAnimation) {
|
||||
node.style.height = `0px`
|
||||
setTimeout(() => {
|
||||
onClose?.()
|
||||
}, 100)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CSSTransition>
|
||||
)
|
||||
|
||||
return {
|
||||
getCollapsibleElms,
|
||||
collapsed,
|
||||
setCollapsed,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user