1
1
import type { Interpolation , Theme } from "@emotion/react" ;
2
- import type { FC } from "react" ;
2
+ import { Button } from "components/Button/Button" ;
3
+ import {
4
+ Tooltip ,
5
+ TooltipContent ,
6
+ TooltipProvider ,
7
+ TooltipTrigger ,
8
+ } from "components/Tooltip/Tooltip" ;
9
+ import { EyeIcon , EyeOffIcon } from "lucide-react" ;
10
+ import { type FC , useState } from "react" ;
3
11
import { MONOSPACE_FONT_FAMILY } from "theme/constants" ;
4
12
import { CopyButton } from "../CopyButton/CopyButton" ;
5
13
6
14
interface CodeExampleProps {
7
15
code :string ;
16
+ /** Defaulting to true to be on the safe side; you should have to opt out of the secure option, not remember to opt in */
8
17
secret ?:boolean ;
18
+ /** Redact parts of the code if the user doesn't want to obfuscate the whole code */
19
+ redactPattern ?:RegExp ;
20
+ /** Replacement text for redacted content */
21
+ redactReplacement ?:string ;
22
+ /** Show a button to reveal the redacted parts of the code */
23
+ showRevealButton ?:boolean ;
9
24
className ?:string ;
10
25
}
11
26
@@ -15,11 +30,28 @@ interface CodeExampleProps {
15
30
export const CodeExample :FC < CodeExampleProps > = ( {
16
31
code,
17
32
className,
18
-
19
- // Defaulting to true to be on the safe side; you should have to opt out of
20
- // the secure option, not remember to opt in
21
33
secret= true ,
34
+ redactPattern,
35
+ redactReplacement= "********" ,
36
+ showRevealButton,
22
37
} ) => {
38
+ const [ showFullValue , setShowFullValue ] = useState ( false ) ;
39
+
40
+ const displayValue = secret
41
+ ?obfuscateText ( code )
42
+ :redactPattern && ! showFullValue
43
+ ?code . replace ( redactPattern , redactReplacement )
44
+ :code ;
45
+
46
+ const showButtonLabel = showFullValue
47
+ ?"Hide sensitive data"
48
+ :"Show sensitive data" ;
49
+ const icon = showFullValue ?(
50
+ < EyeOffIcon className = "h-4 w-4" />
51
+ ) :(
52
+ < EyeIcon className = "h-4 w-4" />
53
+ ) ;
54
+
23
55
return (
24
56
< div css = { styles . container } className = { className } >
25
57
< code css = { [ styles . code , secret && styles . secret ] } >
@@ -33,17 +65,36 @@ export const CodeExample: FC<CodeExampleProps> = ({
33
65
* 2. Even with it turned on and supported, the plaintext is still
34
66
* readily available in the HTML itself
35
67
*/ }
36
- < span aria-hidden > { obfuscateText ( code ) } </ span >
68
+ < span aria-hidden > { displayValue } </ span >
37
69
< span className = "sr-only" >
38
70
Encrypted text. Please access via the copy button.
39
71
</ span >
40
72
</ >
41
73
) :(
42
- code
74
+ displayValue
43
75
) }
44
76
</ code >
45
77
46
- < CopyButton text = { code } label = "Copy code" />
78
+ < div className = "flex items-center gap-1" >
79
+ { showRevealButton && redactPattern && ! secret && (
80
+ < TooltipProvider >
81
+ < Tooltip >
82
+ < TooltipTrigger asChild >
83
+ < Button
84
+ size = "icon"
85
+ variant = "subtle"
86
+ onClick = { ( ) => setShowFullValue ( ! showFullValue ) }
87
+ >
88
+ { icon }
89
+ < span className = "sr-only" > { showButtonLabel } </ span >
90
+ </ Button >
91
+ </ TooltipTrigger >
92
+ < TooltipContent > { showButtonLabel } </ TooltipContent >
93
+ </ Tooltip >
94
+ </ TooltipProvider >
95
+ ) }
96
+ < CopyButton text = { code } label = "Copy code" />
97
+ </ div >
47
98
</ div >
48
99
) ;
49
100
} ;