Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@
},
"CopyKubeconfigButton": {
"kubeconfigButton": "Kubeconfig",
"copiedMessage": "Copied to Clipboard",
"failedMessage": "Failed to copy, Error:",
"menuDownload": "Download",
"menuCopy": "Copy to clipboard"
},
Expand Down Expand Up @@ -205,10 +203,6 @@
"treeExecution": "Execution",
"noExecutionFound": "No Executions found"
},
"CopyButton": {
"copiedMessage": "Copied To Clipboard",
"failedMessage": "Failed to copy"
},
"DeleteWorkspaceDialog": {
"title": "Delete a Workspace",
"introSection1": "The below instructions will help you delete the workspace \"{{workspaceName}}\" from project namespace \"{{projectNamespace}}\" using kubectl.",
Expand Down Expand Up @@ -341,7 +335,9 @@
"unhealthy": "Unhealthy",
"progress": "Managed",
"remaining": "Remaining",
"active": "Active"
"active": "Active",
"copyToClipboardSuccessToast": "Copied to clipboard",
"copyToClipboardFailedToast": "Failed to copy to clipboard"
},
"errors": {
"installError": "Install error",
Expand All @@ -360,7 +356,6 @@
"cancel": "Cancel"
},
"yaml": {
"copiedToClipboard": "YAML copied to clipboard!",
"YAML": "File"
},
"createMCP": {
Expand Down
13 changes: 3 additions & 10 deletions src/components/ControlPlanes/CopyKubeconfigButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, Menu, MenuItem } from '@ui5/webcomponents-react';
import { useToast } from '../../context/ToastContext.tsx';
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
import { useRef, useState } from 'react';
import '@ui5/webcomponents-icons/dist/copy';
import '@ui5/webcomponents-icons/dist/accept';
Expand All @@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
export default function CopyKubeconfigButton() {
const popoverRef = useRef(null);
const [open, setOpen] = useState(false);
const { show } = useToast();
const { copyToClipboard } = useCopyToClipboard();
const { t } = useTranslation();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -37,14 +37,7 @@ export default function CopyKubeconfigButton() {
return;
}
if (event.detail.item.dataset.action === 'copy') {
try {
navigator.clipboard.writeText(mcp.kubeconfig ?? '');
show(t('CopyKubeconfigButton.copiedMessage'));
} catch (error) {
//TODO: handle error, show error to user
show(`${t('CopyKubeconfigButton.failedMessage')} ${error}`);
console.error(error);
}
void copyToClipboard(mcp.kubeconfig ?? '');
}

setOpen(false);
Expand Down
17 changes: 3 additions & 14 deletions src/components/Dialogs/KubectlCommandInfo/KubectlTerminal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FlexBox, Button } from '@ui5/webcomponents-react';
import { useToast } from '../../../context/ToastContext';
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard.ts';
import '@ui5/webcomponents-icons/dist/copy';
import { ThemingParameters } from '@ui5/webcomponents-react-base';

Expand All @@ -8,18 +8,7 @@ interface KubeCtlTerminalProps {
}

export const KubectlTerminal = ({ command }: KubeCtlTerminalProps) => {
const { show } = useToast();

const handleCopy = () => {
navigator.clipboard.writeText(command).then(
() => {
show('Command copied to clipboard');
},
(err) => {
console.error('Could not copy text: ', err);
},
);
};
const { copyToClipboard } = useCopyToClipboard();

const FormattedCommand = () => {
if (command.startsWith("echo '") && command.includes('apiVersion:')) {
Expand Down Expand Up @@ -91,7 +80,7 @@ export const KubectlTerminal = ({ command }: KubeCtlTerminalProps) => {
}}
/>
</FlexBox>
<Button icon="copy" design="Transparent" tooltip="Copy to clipboard" onClick={handleCopy} />
<Button icon="copy" design="Transparent" tooltip="Copy to clipboard" onClick={() => copyToClipboard(command)} />
</FlexBox>

<div style={{ padding: '12px 16px', overflowX: 'auto' }}>
Expand Down
15 changes: 5 additions & 10 deletions src/components/Shared/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, ButtonPropTypes } from '@ui5/webcomponents-react';
import { useToast } from '../../context/ToastContext.tsx';
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
import { useId, CSSProperties } from 'react';
import { useCopyButton } from '../../context/CopyButtonContext.tsx';
import { ThemingParameters } from '@ui5/webcomponents-react-base';
Expand All @@ -11,20 +11,15 @@ interface CopyButtonProps extends ButtonPropTypes {
}

export const CopyButton = ({ text, style = {}, ...buttonProps }: CopyButtonProps) => {
const { show } = useToast();
const { copyToClipboard } = useCopyToClipboard();
const { activeCopyId, setActiveCopyId } = useCopyButton();
const uniqueId = useId();
const isCopied = activeCopyId === uniqueId;
const { t } = useTranslation();

const handleCopy = async () => {
try {
await navigator.clipboard.writeText(text);
setActiveCopyId(uniqueId);
} catch (err) {
console.error(`Failed to copy text: ${text}. Error: ${err}`);
show(`${t('CopyButton.copiedMessage')} ${err}`);
}
await copyToClipboard(text, { showToastOnSuccess: false });
setActiveCopyId(uniqueId);
};

const defaultStyle: CSSProperties = {
Expand All @@ -40,7 +35,7 @@ export const CopyButton = ({ text, style = {}, ...buttonProps }: CopyButtonProps
onClick={handleCopy}
{...buttonProps}
>
{isCopied ? t('CopyButton.copiedMessage') : text}
{isCopied ? t('common.copyToClipboardSuccessToast') : text}
</Button>
);
};
10 changes: 3 additions & 7 deletions src/components/Yaml/YamlViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ import { materialLight, materialDark } from 'react-syntax-highlighter/dist/esm/s

import { Button, FlexBox } from '@ui5/webcomponents-react';
import styles from './YamlViewer.module.css';
import { useToast } from '../../context/ToastContext.tsx';
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
import { useTranslation } from 'react-i18next';
import { useTheme } from '../../hooks/useTheme.ts';
type YamlViewerProps = { yamlString: string; filename: string };
const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename }) => {
const toast = useToast();
const { t } = useTranslation();
const { isDarkTheme } = useTheme();
const copyToClipboard = () => {
navigator.clipboard.writeText(yamlString);
toast.show(t('yaml.copiedToClipboard'));
};
const { copyToClipboard } = useCopyToClipboard();
const downloadYaml = () => {
const blob = new Blob([yamlString], { type: 'text/yaml' });
const url = window.URL.createObjectURL(blob);
Expand All @@ -31,7 +27,7 @@ const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename }) => {
return (
<div className={styles.container}>
<FlexBox className={styles.buttons} direction="Row" justifyContent="End" alignItems="Baseline" gap={16}>
<Button icon="copy" onClick={copyToClipboard}>
<Button icon="copy" onClick={() => copyToClipboard(yamlString)}>
{t('buttons.copy')}
</Button>
<Button icon="download" onClick={downloadYaml}>
Expand Down
34 changes: 34 additions & 0 deletions src/hooks/useCopyToClipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useCallback } from 'react';
import { useToast } from '../context/ToastContext.tsx';
import { useTranslation } from 'react-i18next';

export type CopyFn = (text: string, options?: { showToastOnSuccess: boolean }) => Promise<boolean>;

export function useCopyToClipboard(): { copyToClipboard: CopyFn } {
const toast = useToast();
const { t } = useTranslation();

const copyToClipboard: CopyFn = useCallback(
async (text, options = { showToastOnSuccess: true }) => {
if (!navigator.clipboard) {
toast.show(t('common.copyToClipboardFailedToast'));
return false;
}

try {
await navigator.clipboard.writeText(text);
if (options.showToastOnSuccess) {
toast.show(t('common.copyToClipboardSuccessToast'));
}
return true;
} catch (error) {
toast.show(t('common.copyToClipboardFailedToast'));
console.error(error);
return false;
}
},
[toast, t],
);

return { copyToClipboard };
}
Loading