fix: copy button do not work
This commit is contained in:
@@ -1,58 +1,137 @@
|
|||||||
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 { Typography } from 'antd';
|
import { Button, message, Tooltip } from 'antd';
|
||||||
import { TextProps } from 'antd/es/typography/Text';
|
import React, {
|
||||||
import React, { useMemo } from 'react';
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
|
import AutoTooltip from '../auto-tooltip';
|
||||||
|
|
||||||
type CopyButtonProps = {
|
type CopyButtonProps = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
text: string;
|
text: string;
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
|
type?: 'text' | 'primary' | 'dashed' | 'link' | 'default';
|
||||||
|
size?: 'small' | 'middle' | 'large';
|
||||||
|
shape?: 'circle' | 'round' | 'default';
|
||||||
|
tips?: string;
|
||||||
|
placement?:
|
||||||
|
| 'top'
|
||||||
|
| 'left'
|
||||||
|
| 'right'
|
||||||
|
| 'bottom'
|
||||||
|
| 'topLeft'
|
||||||
|
| 'topRight'
|
||||||
|
| 'bottomLeft'
|
||||||
|
| 'bottomRight';
|
||||||
btnStyle?: React.CSSProperties;
|
btnStyle?: React.CSSProperties;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
format?: 'text/plain' | 'text/html';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const CopyButton: React.FC<CopyButtonProps & TextProps> = ({
|
const CopyButton: React.FC<CopyButtonProps> = ({
|
||||||
children,
|
children,
|
||||||
|
tips,
|
||||||
text,
|
text,
|
||||||
|
type = 'text',
|
||||||
|
shape = 'default',
|
||||||
fontSize = '14px',
|
fontSize = '14px',
|
||||||
style,
|
style,
|
||||||
btnStyle,
|
btnStyle,
|
||||||
format = 'text/plain',
|
placement,
|
||||||
...rest
|
size = 'small'
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const timerRef = useRef<number>();
|
||||||
|
|
||||||
const tooltips = useMemo(() => {
|
const resetCopied = () => {
|
||||||
return [
|
window.clearTimeout(timerRef.current);
|
||||||
intl.formatMessage({ id: 'common.button.copy' }),
|
timerRef.current = window.setTimeout(() => {
|
||||||
intl.formatMessage({ id: 'common.button.copied' })
|
setCopied(false);
|
||||||
];
|
}, 3000);
|
||||||
}, [intl]);
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fallback:execCommand(old Safari / NON-HTTPS)
|
||||||
|
*/
|
||||||
|
const legacyCopy = (value: string) => {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = value;
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
textarea.style.opacity = '0';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopy = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
if (navigator.clipboard?.writeText) {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
} else {
|
||||||
|
const success = legacyCopy(text);
|
||||||
|
if (!success) throw new Error('legacy copy failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
setCopied(true);
|
||||||
|
} catch {
|
||||||
|
message.error(intl.formatMessage({ id: 'common.copy.fail' }) as string);
|
||||||
|
}
|
||||||
|
}, [text, intl]);
|
||||||
|
|
||||||
|
const tipTitle = useMemo(() => {
|
||||||
|
if (copied) {
|
||||||
|
return intl.formatMessage({ id: 'common.button.copied' });
|
||||||
|
}
|
||||||
|
return tips ?? intl.formatMessage({ id: 'common.button.copy' });
|
||||||
|
}, [copied, tips, intl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resetCopied();
|
||||||
|
}, [copied]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typography.Paragraph
|
<div className="flex-center gap-4" style={{ minWidth: 16 }}>
|
||||||
style={{ ...btnStyle, marginBottom: 0 }}
|
{children && (
|
||||||
{...rest}
|
<AutoTooltip minWidth={20} ghost>
|
||||||
copyable={{
|
{children}
|
||||||
text: text,
|
</AutoTooltip>
|
||||||
format: format,
|
)}
|
||||||
tooltips: [
|
<Tooltip title={tipTitle} placement={placement}>
|
||||||
intl.formatMessage({ id: 'common.button.copy' }),
|
<span>
|
||||||
intl.formatMessage({ id: 'common.button.copied' })
|
<Button
|
||||||
],
|
className="copy-button"
|
||||||
icon: [
|
type={type}
|
||||||
<CopyOutlined style={{ fontSize: fontSize, ...style }} key="copy" />,
|
shape={shape}
|
||||||
<CheckCircleFilled
|
size={size}
|
||||||
style={{ color: 'var(--ant-color-success)', fontSize: fontSize }}
|
onClick={handleCopy}
|
||||||
key="copied"
|
style={{ ...btnStyle }}
|
||||||
/>
|
icon={
|
||||||
]
|
copied ? (
|
||||||
}}
|
<CheckCircleFilled
|
||||||
>
|
style={{
|
||||||
{children}
|
color: 'var(--ant-color-success)',
|
||||||
</Typography.Paragraph>
|
fontSize
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CopyOutlined style={{ fontSize, ...style }} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></Button>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -285,7 +285,14 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
<SealInput.Input
|
<SealInput.Input
|
||||||
label={intl.formatMessage({ id: 'apikeys.form.apikey' })}
|
label={intl.formatMessage({ id: 'apikeys.form.apikey' })}
|
||||||
value={apikeyValue}
|
value={apikeyValue}
|
||||||
addAfter={<CopyButton text={apikeyValue}></CopyButton>}
|
addAfter={
|
||||||
|
<CopyButton
|
||||||
|
text={apikeyValue}
|
||||||
|
shape="default"
|
||||||
|
size="middle"
|
||||||
|
type="text"
|
||||||
|
></CopyButton>
|
||||||
|
}
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ const RowWrapper = styled.div`
|
|||||||
|
|
||||||
const InfoItem = styled.div`
|
const InfoItem = styled.div`
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: max-content 1fr;
|
align-items: center;
|
||||||
|
grid-template-columns: max-content minmax(0, 1fr);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
.label {
|
.label {
|
||||||
color: var(--ant-color-text-tertiary);
|
color: var(--ant-color-text-tertiary);
|
||||||
@@ -136,12 +137,7 @@ export const VersionItem: React.FC<VersionItemProps> = ({ data }) => {
|
|||||||
<span className="label">
|
<span className="label">
|
||||||
{intl.formatMessage({ id: 'backend.imageName' })}:
|
{intl.formatMessage({ id: 'backend.imageName' })}:
|
||||||
</span>
|
</span>
|
||||||
<CopyButton
|
<CopyButton text={data.image_name} type="link">
|
||||||
text={data.image_name}
|
|
||||||
ellipsis={{
|
|
||||||
tooltip: !data.is_built_in ? data.image_name : false
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{data.is_built_in
|
{data.is_built_in
|
||||||
? intl.formatMessage({ id: 'backend.versionInfo.autoImage' })
|
? intl.formatMessage({ id: 'backend.versionInfo.autoImage' })
|
||||||
: data.image_name}
|
: data.image_name}
|
||||||
|
|||||||
Reference in New Issue
Block a user