-
Notifications
You must be signed in to change notification settings - Fork 7.7k
/
Copy pathTerminalBlock.tsx
96 lines (88 loc) · 2.66 KB
/
TerminalBlock.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {isValidElement, useState, useEffect} from 'react';
import * as React from 'react';
import {IconTerminal} from '../Icon/IconTerminal';
import {IconCopy} from 'components/Icon/IconCopy';
type LogLevel = 'info' | 'warning' | 'error';
interface TerminalBlockProps {
level?: LogLevel;
children: React.ReactNode;
customHeader?: React.ReactNode;
}
function LevelText({type}: {type: LogLevel}) {
switch (type) {
case 'warning':
return <span className="text-yellow-50 bg-none me-1">Warning: </span>;
case 'error':
return <span className="text-red-40 me-1">Error: </span>;
default:
return null;
}
}
function TerminalBlock({
level = 'info',
children,
customHeader,
}: TerminalBlockProps) {
let message: string | undefined;
if (typeof children === 'string') {
message = children;
} else if (
isValidElement(children) &&
typeof (children as React.ReactElement<{children: string}>).props
.children === 'string'
) {
message = (children as React.ReactElement<{children: string}>).props
.children;
} else {
throw Error('Expected TerminalBlock children to be a plain string.');
}
const [copied, setCopied] = useState(false);
useEffect(() => {
if (!copied) {
return;
} else {
const timer = setTimeout(() => {
setCopied(false);
}, 2000);
return () => clearTimeout(timer);
}
}, [copied]);
return (
<div className="rounded-lg bg-secondary dark:bg-gray-50 h-full my-4">
<div className="bg-gray-90 dark:bg-gray-60 w-full rounded-t-lg">
<div className="text-primary-dark dark:text-primary-dark flex text-sm px-4 py-0.5 relative justify-between">
<div>
{customHeader || (
<>
<IconTerminal className="inline-flex me-2 self-center" />{' '}
Terminal
</>
)}
</div>
<div>
<button
className="w-full text-start text-primary-dark dark:text-primary-dark"
onClick={() => {
window.navigator.clipboard.writeText(message ?? '');
setCopied(true);
}}>
<IconCopy className="inline-flex me-2 self-center" />{' '}
{copied ? 'Copied' : 'Copy'}
</button>
</div>
</div>
</div>
<div
className="px-8 pt-4 pb-6 text-primary-dark dark:text-primary-dark font-mono text-code whitespace-pre overflow-x-auto"
translate="no"
dir="ltr">
<LevelText type={level} />
{message}
</div>
</div>
);
}
export default TerminalBlock;