fix: copybutton do not work
This commit is contained in:
@@ -1,13 +1,7 @@
|
|||||||
import { CheckCircleFilled, CopyOutlined } from '@ant-design/icons';
|
import { CheckCircleFilled, CopyOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, message, Tooltip } from 'antd';
|
import { Button, message, Tooltip } from 'antd';
|
||||||
import React, {
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState
|
|
||||||
} from 'react';
|
|
||||||
import AutoTooltip from '../auto-tooltip';
|
import AutoTooltip from '../auto-tooltip';
|
||||||
|
|
||||||
type CopyButtonProps = {
|
type CopyButtonProps = {
|
||||||
@@ -55,39 +49,63 @@ const CopyButton: React.FC<CopyButtonProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fallback:execCommand(old Safari / NON-HTTPS)
|
* Modern clipboard API (works in secure contexts: HTTPS or localhost)
|
||||||
*/
|
*/
|
||||||
const legacyCopy = (value: string) => {
|
const asyncCopy = async (value: string): Promise<boolean> => {
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = value;
|
|
||||||
textarea.style.position = 'fixed';
|
|
||||||
textarea.style.opacity = '0';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
try {
|
||||||
document.execCommand('copy');
|
await navigator.clipboard.writeText(value);
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = useCallback(async () => {
|
/**
|
||||||
|
* Fallback: execCommand with copy event listener
|
||||||
|
* More reliable than textarea selection method
|
||||||
|
*/
|
||||||
|
const execCopy = (value: string): boolean => {
|
||||||
|
let copySuccess = false;
|
||||||
|
|
||||||
|
const onCopy = (event: ClipboardEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
event.clipboardData?.clearData();
|
||||||
|
event.clipboardData?.setData('text/plain', value);
|
||||||
|
copySuccess = true;
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (navigator.clipboard?.writeText) {
|
document.addEventListener('copy', onCopy, { capture: true });
|
||||||
await navigator.clipboard.writeText(text);
|
document.execCommand('copy');
|
||||||
} else {
|
return copySuccess;
|
||||||
const success = legacyCopy(text);
|
} catch (error) {
|
||||||
if (!success) throw new Error('legacy copy failed');
|
return false;
|
||||||
|
} finally {
|
||||||
|
document.removeEventListener('copy', onCopy, { capture: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
// Try modern clipboard API first
|
||||||
|
if (await asyncCopy(text)) {
|
||||||
|
setCopied(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCopied(true);
|
// Fallback to execCommand method
|
||||||
} catch {
|
if (execCopy(text)) {
|
||||||
|
setCopied(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both methods failed
|
||||||
|
throw new Error('Copy failed');
|
||||||
|
} catch (error) {
|
||||||
message.error(intl.formatMessage({ id: 'common.copy.fail' }) as string);
|
message.error(intl.formatMessage({ id: 'common.copy.fail' }) as string);
|
||||||
}
|
}
|
||||||
}, [text, intl]);
|
};
|
||||||
|
|
||||||
const tipTitle = useMemo(() => {
|
const tipTitle = useMemo(() => {
|
||||||
if (copied) {
|
if (copied) {
|
||||||
|
|||||||
Reference in New Issue
Block a user