fix: scroll code containers so copy button does not overlay

Closes #19

Luke Karrys committed Sep 29, 2023 at 13:35 UTC 07c12792539fe5ad1899320faca8937dc1dab576
2 files changed +29 -26
theme/gatsby-config.js
+3 -1
@@ -20,7 +20,9 @@ module.exports = themeOptions => {
20 resolve: 'gatsby-source-filesystem',
21 options: {
22 name: 'content',
23 - path: path.resolve('./content'),
23 + path: process.env.GATSBY_PARTIAL_CONTENT
24 + ? path.resolve(`./content/${process.env.GATSBY_PARTIAL_CONTENT}`)
25 + : path.resolve('./content'),
26 },
27 },
28 {
theme/src/components/code.js
+26 -25
@@ -9,37 +9,40 @@ import LiveCode from './live-code'
9 * Resize the scroll handle to the size of the code contents, since the former has to be positioned absolutely.
10 */
11 const useScrollSize = () => {
12 - const ref = React.createRef()
12 + const scrollRef = React.createRef()
13 + const paddingRef = React.createRef()
14 const [size, setSize] = useState({})
15
16 useEffect(() => {
16 - // Skip if already resized.
17 - if (typeof size.width !== 'undefined') {
17 + const scrollNode = scrollRef.current
18 + const paddingNode = paddingRef.current
19 +
20 + if (!scrollNode || !paddingNode || typeof size.width !== 'undefined') {
21 return
22 }
23
21 - const node = ref.current
22 - node.parentElement.style.position = 'relative'
23 - const computedStyle = getComputedStyle(node.parentElement)
24 + const parent = scrollNode.parentElement
25 + const button = paddingNode.firstChild
26 +
27 + parent.style.position = 'relative'
28 + const parentStyle = getComputedStyle(parent)
29 + const paddingTop = parseInt(parentStyle.paddingTop, 10)
30 + const paddingBottom = parseInt(parentStyle.paddingBottom, 10)
31 + const paddingRight = parseInt(parentStyle.paddingRight, 10)
32 +
33 setSize({
25 - height:
26 - node.parentElement.clientHeight -
27 - parseInt(computedStyle.paddingTop, 10) -
28 - parseInt(computedStyle.paddingBottom, 10),
29 - width:
30 - node.parentElement.scrollWidth -
31 - parseInt(computedStyle.paddingLeft, 10) -
32 - parseInt(computedStyle.paddingRight, 10),
34 + height: parent.clientHeight - paddingTop - paddingBottom,
35 + width: parent.scrollWidth - paddingRight + button.clientWidth,
36 })
34 - }, [size, ref])
37 + }, [scrollRef, paddingRef, size])
38
36 - return [ref, size]
39 + return {scrollRef, paddingRef, size}
40 }
41
42 function Code({className, children, live, noinline}) {
43 const language = className ? className.replace(/language-/, '') : ''
44 const code = children.trim()
42 - const [scrollHandleRef, scrollSize] = useScrollSize()
45 + const {scrollRef, paddingRef, size} = useScrollSize()
46
47 if (live) {
48 return <LiveCode code={code} language={language} noinline={noinline} />
@@ -47,18 +50,16 @@ function Code({className, children, live, noinline}) {
50
51 return (
52 <Relative>
50 - <Absolute top={0} right={0} p={2} zIndex={1}>
51 - <ClipboardCopy value={code} />
52 - </Absolute>
53 + <div ref={paddingRef}>
54 + <Absolute top={0} right={0} p={2} zIndex={1}>
55 + <ClipboardCopy value={code} />
56 + </Absolute>
57 + </div>
58 <Highlight {...defaultProps} code={code} language={language} theme={githubTheme}>
59 {({className, style, tokens, getLineProps, getTokenProps}) => (
60 <BorderBox as="pre" className={className} mt={0} mb={3} p={3} border={0} style={{...style, overflow: 'auto'}}>
61 {/* This is the scroll handle, it is supposed to be focused with keyboard and scroll a wide codebox horizontally */}
57 - <div
58 - aria-hidden="true"
59 - style={{visibility: 'hidden', position: 'absolute', ...scrollSize}}
60 - ref={scrollHandleRef}
61 - />
62 + <div aria-hidden="true" style={{visibility: 'hidden', position: 'absolute', ...size}} ref={scrollRef} />
63 {tokens.map((line, i) => (
64 <div key={i} {...getLineProps({line, key: i})}>
65 {line.map((token, key) => (