CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/components/copy-to-clipboard.tsx
Views: 923
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { CopyOutlined } from "@ant-design/icons";
7
import { Button, Input, Space, Tooltip } from "antd";
8
import { ReactNode, useEffect, useMemo, useState } from "react";
9
import { CopyToClipboard } from "react-copy-to-clipboard";
10
11
import { CSS } from "@cocalc/frontend/app-framework";
12
13
interface Props {
14
value: string;
15
display?: string;
16
style?: CSS;
17
label?: ReactNode;
18
labelStyle?: CSS;
19
inputStyle?: CSS;
20
outerStyle?: CSS;
21
inputWidth?: string;
22
size?: "large" | "middle" | "small";
23
before?: boolean;
24
copyTip?: string;
25
}
26
27
const INPUT_STYLE: CSS = { display: "inline-block", flex: 1 } as const;
28
29
const LABEL_STYLE: CSS = {
30
marginRight: "15px",
31
display: "flex",
32
flex: "0 0 auto",
33
justifyContent: "center",
34
alignContent: "center",
35
flexDirection: "column",
36
} as const;
37
38
export default function CopyToClipBoard({
39
value,
40
display,
41
style,
42
size,
43
label,
44
labelStyle,
45
inputStyle,
46
outerStyle,
47
inputWidth,
48
copyTip,
49
before,
50
}: Props) {
51
const [copied, setCopied] = useState<boolean>(false);
52
53
useEffect(() => {
54
setCopied(false);
55
}, [value]);
56
57
let copy = useMemo(() => {
58
const btn = (
59
<CopyToClipboard text={value} onCopy={() => setCopied(true)}>
60
<Button size={size} icon={<CopyOutlined />} />
61
</CopyToClipboard>
62
);
63
if (!copied) return btn;
64
return (
65
<Tooltip title={copyTip ?? "Copied!"} defaultOpen>
66
{btn}
67
</Tooltip>
68
);
69
}, [value, copied, size]);
70
71
// ws: See https://ant.design/components/input for why using Input.Group is the
72
// right way to do this.
73
// hsy: Input.Group is deprecated, using Space.Compact instead
74
const input = (
75
<Space.Compact style={outerStyle}>
76
{before ? copy : undefined}
77
<Input
78
style={{
79
width: inputWidth ?? `${(display ?? value).length + 8}ex`,
80
fontFamily: "monospace",
81
...inputStyle,
82
}}
83
readOnly
84
size={size}
85
value={display ?? value}
86
onFocus={(e) => e.target.select()}
87
/>
88
{!before ? copy : undefined}
89
</Space.Compact>
90
);
91
if (!label) {
92
return <div style={style}>{input}</div>;
93
}
94
return (
95
<div style={{ display: "flex", ...style }}>
96
<div style={{ ...LABEL_STYLE, ...labelStyle }}>{label}</div>{" "}
97
<div style={{ ...INPUT_STYLE }}>{input}</div>
98
</div>
99
);
100
}
101
102