Use CSS only method to scroll pre

Luke Karrys committed Oct 17, 2023 at 14:02 UTC 873931631bc458ba4d0f56370257d54c7b9f4a66
2 files changed +77 -68
src/hooks/use-scroll-size.js deleted
-37
@@ -1,37 +0,0 @@
1 -import {createRef, useState, useEffect} from 'react'
2 -
3 -/**
4 - * Resize the scroll handle to the size of the code contents, since the former has to be positioned absolutely.
5 - */
6 -const useScrollSize = () => {
7 - const scrollRef = createRef()
8 - const paddingRef = createRef()
9 - const [size, setSize] = useState({})
10 -
11 - useEffect(() => {
12 - const scrollNode = scrollRef.current
13 - const paddingNode = paddingRef.current
14 -
15 - if (!scrollNode || !paddingNode || typeof size.width !== 'undefined') {
16 - return
17 - }
18 -
19 - const parent = scrollNode.parentElement
20 - const button = paddingNode.firstChild
21 -
22 - parent.style.position = 'relative'
23 - const parentStyle = getComputedStyle(parent)
24 - const paddingTop = parseInt(parentStyle.paddingTop, 10)
25 - const paddingBottom = parseInt(parentStyle.paddingBottom, 10)
26 - const paddingRight = parseInt(parentStyle.paddingRight, 10)
27 -
28 - setSize({
29 - height: parent.clientHeight - paddingTop - paddingBottom,
30 - width: parent.scrollWidth - paddingRight + button.clientWidth,
31 - })
32 - }, [scrollRef, paddingRef, size])
33 -
34 - return {scrollRef, paddingRef, size}
35 -}
36 -
37 -export default useScrollSize
src/mdx/code.js
+77 -31
@@ -1,52 +1,98 @@
1 import React from 'react'
2 -import {Box, Text} from '@primer/react'
2 +import {Box, Text, Button, Octicon} from '@primer/react'
3 import Highlight, {defaultProps} from 'prism-react-renderer'
4 import githubTheme from 'prism-react-renderer/themes/github'
5 -import ClipboardCopy from '../components/clipboard-copy'
6 -import useScrollSize from '../hooks/use-scroll-size'
5 +import {CheckIcon, CopyIcon} from '@primer/octicons-react'
6 +import copy from 'copy-to-clipboard'
7 +import {announce} from '../util/aria-live'
8
8 -function Code({className: language = '', children}) {
9 +function ClipboardCopy({value, ...props}) {
10 + const [copied, setCopied] = React.useState(false)
11 +
12 + React.useEffect(() => {
13 + const timeout = setTimeout(() => {
14 + if (copied) {
15 + setCopied(false)
16 + }
17 + }, 1000)
18 +
19 + return () => clearTimeout(timeout)
20 + }, [copied])
21 +
22 + return (
23 + <Button
24 + {...props}
25 + aria-label="Copy to clipboard"
26 + onClick={() => {
27 + copy(value)
28 + setCopied(true)
29 + announce(`Copied to clipboard`)
30 + }}
31 + >
32 + <Octicon icon={copied ? CheckIcon : CopyIcon} sx={{color: copied ? 'success.fg' : 'fg.muted'}} />
33 + </Button>
34 + )
35 +}
36 +
37 +// export default ClipboardCopy
38 +
39 +function Code({className, children}) {
40 + const language = className ? className.replace(/language-/, '') : ''
41 const code = children.trim()
10 - const {scrollRef, paddingRef, size} = useScrollSize()
42
43 return (
13 - <Box sx={{position: 'relative'}}>
14 - <div ref={paddingRef}>
15 - <Box sx={{position: 'absolute', top: 0, right: 0, p: 2, zIndex: 1}}>
16 - <ClipboardCopy value={code} />
17 - </Box>
18 - </div>
19 - <Highlight {...defaultProps} code={code} language={language.replace(/language-/, '')} theme={githubTheme}>
20 - {({className, style, tokens, getLineProps, getTokenProps}) => (
44 + <Highlight {...defaultProps} code={code} language={language} theme={githubTheme}>
45 + {({className, style, tokens, getLineProps, getTokenProps}) => (
46 + <Box
47 + sx={{
48 + // Make <pre> adjust to the width of the container
49 + // https://stackoverflow.com/a/14406386
50 + display: 'table',
51 + tableLayout: 'fixed',
52 + width: '100%',
53 + mb: 3,
54 + }}
55 + >
56 <Box
22 - tabIndex={0}
23 - as="pre"
24 - className={className}
57 style={style}
58 sx={{
27 - overflow: 'auto',
59 + display: 'flex',
60 + justifyContent: 'space-between',
61 + flexDirection: 'row-reverse',
62 borderRadius: 2,
29 - mt: 0,
30 - mb: 3,
31 - p: 3,
63 borderStyle: 'solid',
64 borderWidth: 1,
65 borderColor: 'border.muted',
66 }}
67 >
37 - {/* This is the scroll handle, it is supposed to be focused with keyboard and scroll a wide codebox horizontally */}
38 - <div aria-hidden="true" style={{visibility: 'hidden', position: 'absolute', ...size}} ref={scrollRef} />
39 - {tokens.map((line, i) => (
40 - <div key={i} {...getLineProps({line, key: i})}>
41 - {line.map((token, key) => (
42 - <Text key={key} {...getTokenProps({token, key})} sx={{fontFamily: 'mono', fontSize: 1}} />
68 + <ClipboardCopy
69 + value={code}
70 + sx={{
71 + borderRadius: 0,
72 + borderStyle: 'solid',
73 + borderWidth: 1,
74 + borderColor: 'border.muted',
75 + marginTop: '-1px',
76 + marginRight: '-1px',
77 + borderTopRightRadius: 2,
78 + borderBottomLeftRadius: 2,
79 + }}
80 + />
81 + <Box sx={{m: 0, p: 3, overflowX: 'auto'}}>
82 + <Box as="pre" className={className} tabIndex={0} sx={{m: 0}}>
83 + {tokens.map((line, i) => (
84 + <div key={i} {...getLineProps({line, key: i})}>
85 + {line.map((token, key) => (
86 + <Text key={key} {...getTokenProps({token, key})} sx={{fontFamily: 'mono', fontSize: 1}} />
87 + ))}
88 + </div>
89 ))}
44 - </div>
45 - ))}
90 + </Box>
91 + </Box>
92 </Box>
47 - )}
48 - </Highlight>
49 - </Box>
93 + </Box>
94 + )}
95 + </Highlight>
96 )
97 }
98