mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import IconButton from "@material-ui/core/Button"
|
|
import { makeStyles } from "@material-ui/core/styles"
|
|
import Tooltip from "@material-ui/core/Tooltip"
|
|
import Check from "@material-ui/icons/Check"
|
|
import React, { useState } from "react"
|
|
import { combineClasses } from "../../util/combineClasses"
|
|
import { FileCopyIcon } from "../Icons/FileCopyIcon"
|
|
|
|
interface CopyButtonProps {
|
|
text: string
|
|
ctaCopy?: string
|
|
wrapperClassName?: string
|
|
buttonClassName?: string
|
|
tooltipTitle?: string
|
|
}
|
|
|
|
export const Language = {
|
|
tooltipTitle: "Copy to clipboard",
|
|
ariaLabel: "Copy to clipboard",
|
|
}
|
|
|
|
/**
|
|
* Copy button used inside the CodeBlock component internally
|
|
*/
|
|
export const CopyButton: React.FC<React.PropsWithChildren<CopyButtonProps>> = ({
|
|
text,
|
|
ctaCopy,
|
|
wrapperClassName = "",
|
|
buttonClassName = "",
|
|
tooltipTitle = Language.tooltipTitle,
|
|
}) => {
|
|
const styles = useStyles()
|
|
const [isCopied, setIsCopied] = useState<boolean>(false)
|
|
|
|
const copyToClipboard = async (): Promise<void> => {
|
|
try {
|
|
await window.navigator.clipboard.writeText(text)
|
|
setIsCopied(true)
|
|
window.setTimeout(() => {
|
|
setIsCopied(false)
|
|
}, 1000)
|
|
} catch (err) {
|
|
const input = document.createElement("input")
|
|
input.value = text
|
|
document.body.appendChild(input)
|
|
input.focus()
|
|
input.select()
|
|
const result = document.execCommand("copy")
|
|
document.body.removeChild(input)
|
|
if (result) {
|
|
setIsCopied(true)
|
|
window.setTimeout(() => {
|
|
setIsCopied(false)
|
|
}, 1000)
|
|
} else {
|
|
const wrappedErr = new Error("copyToClipboard: failed to copy text to clipboard")
|
|
if (err instanceof Error) {
|
|
wrappedErr.stack = err.stack
|
|
}
|
|
console.error(wrappedErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Tooltip title={tooltipTitle} placement="top">
|
|
<div className={combineClasses([styles.copyButtonWrapper, wrapperClassName])}>
|
|
<IconButton
|
|
className={combineClasses([styles.copyButton, buttonClassName])}
|
|
onClick={copyToClipboard}
|
|
size="small"
|
|
aria-label={Language.ariaLabel}
|
|
>
|
|
{isCopied ? (
|
|
<Check className={styles.fileCopyIcon} />
|
|
) : (
|
|
<FileCopyIcon className={styles.fileCopyIcon} />
|
|
)}
|
|
{ctaCopy && <div className={styles.buttonCopy}>{ctaCopy}</div>}
|
|
</IconButton>
|
|
</div>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
copyButtonWrapper: {
|
|
display: "flex",
|
|
marginLeft: theme.spacing(1),
|
|
},
|
|
copyButton: {
|
|
borderRadius: theme.shape.borderRadius,
|
|
padding: theme.spacing(0.85),
|
|
minWidth: 32,
|
|
|
|
"&:hover": {
|
|
background: theme.palette.background.paper,
|
|
},
|
|
},
|
|
fileCopyIcon: {
|
|
width: 20,
|
|
height: 20,
|
|
},
|
|
buttonCopy: {
|
|
marginLeft: theme.spacing(1),
|
|
},
|
|
}))
|