feat: ajoute composant CopyButton

This commit is contained in:
2025-07-23 19:45:06 +04:00
parent 1ce038054e
commit 7831ab473b
+130
View File
@@ -0,0 +1,130 @@
import {useState} from 'react'
import PropTypes from 'prop-types'
import IconButton from '@mui/material/IconButton'
import Tooltip from '@mui/material/Tooltip'
import Snackbar from '@mui/material/Snackbar'
import Alert from '@mui/material/Alert'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import CheckIcon from '@mui/icons-material/Check'
export default function CopyButton({
content,
label = 'Copier',
size = 'small',
hasSnackbarVisible = true,
onCopySuccess = null,
onCopyError = null
}) {
const [copied, setCopied] = useState(false)
const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'})
const handleCopy = async () => {
try {
// Modern clipboard API with fallback
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(content)
} else {
// Fallback for older browsers or non-HTTPS
const textArea = document.createElement('textarea')
textArea.value = content
textArea.style.position = 'fixed'
textArea.style.left = '-999999px'
textArea.style.top = '-999999px'
document.body.append(textArea)
textArea.focus()
textArea.select()
const result = document.execCommand('copy')
textArea.remove()
if (!result) {
throw new Error('Copy command failed')
}
}
// Success feedback
setCopied(true)
setTimeout(() => setCopied(false), 2000)
if (hasSnackbarVisible) {
setSnackbar({
open: true,
message: 'Contenu copié dans le presse-papier',
severity: 'success'
})
}
if (onCopySuccess) {
onCopySuccess()
}
} catch (error) {
console.error('Failed to copy to clipboard:', error)
if (hasSnackbarVisible) {
setSnackbar({
open: true,
message: 'Impossible de copier le contenu',
severity: 'error'
})
}
if (onCopyError) {
onCopyError(error)
}
}
}
const handleCloseSnackbar = () => {
setSnackbar(prev => ({...prev, open: false}))
}
return (
<>
<Tooltip title={copied ? 'Copié !' : label}>
<IconButton
size={size}
aria-label={label}
sx={{
color: copied ? 'success.main' : 'text.secondary',
'&:hover': {
color: copied ? 'success.dark' : 'primary.main',
backgroundColor: copied ? 'success.light' : 'action.hover'
},
transition: 'all 0.2s ease-in-out'
}}
onClick={handleCopy}
>
{copied ? <CheckIcon /> : <ContentCopyIcon />}
</IconButton>
</Tooltip>
{hasSnackbarVisible && (
<Snackbar
open={snackbar.open}
autoHideDuration={3000}
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
onClose={handleCloseSnackbar}
>
<Alert
variant='filled'
severity={snackbar.severity}
sx={{width: '100%'}}
onClose={handleCloseSnackbar}
>
{snackbar.message}
</Alert>
</Snackbar>
)}
</>
)
}
CopyButton.propTypes = {
content: PropTypes.string.isRequired,
label: PropTypes.string,
size: PropTypes.oneOf(['small', 'medium', 'large']),
hasSnackbarVisible: PropTypes.bool,
onCopySuccess: PropTypes.func,
onCopyError: PropTypes.func
}