From 7831ab473b56980357664959d7f85e288a43b19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Wed, 23 Jul 2025 19:45:06 +0400 Subject: [PATCH] feat: ajoute composant CopyButton --- components/versions/copy-button.js | 130 +++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 components/versions/copy-button.js diff --git a/components/versions/copy-button.js b/components/versions/copy-button.js new file mode 100644 index 0000000..bb49890 --- /dev/null +++ b/components/versions/copy-button.js @@ -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 ( + <> + + + {copied ? : } + + + + {hasSnackbarVisible && ( + + + {snackbar.message} + + + )} + + ) +} + +CopyButton.propTypes = { + content: PropTypes.string.isRequired, + label: PropTypes.string, + size: PropTypes.oneOf(['small', 'medium', 'large']), + hasSnackbarVisible: PropTypes.bool, + onCopySuccess: PropTypes.func, + onCopyError: PropTypes.func +} +