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