Refactor
Abstract dynamic button to a separate component; Move buttons to the JSONViewer; Move data-saving to a hook;
This commit is contained in:
parent
bab84f5db1
commit
f0b1e9c7d9
6 changed files with 133 additions and 133 deletions
|
@ -36,26 +36,3 @@ $navbar-width: 250px;
|
|||
.react-datepicker-popper {
|
||||
z-index: 30 !important;
|
||||
}
|
||||
|
||||
.topic-message-button {
|
||||
&::after {
|
||||
content: attr(data-title);
|
||||
position: absolute;
|
||||
top: -140%;
|
||||
z-index: 1;
|
||||
background: #F5F5F5;
|
||||
color: rgb(89, 89, 89);
|
||||
border-radius: 5px;
|
||||
font-size: 15px;
|
||||
padding: 5px 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: .2s opacity;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
opacity: 1;
|
||||
transition-delay: .5s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,72 +15,19 @@ const MessageItem: React.FC<MessageItemProp> = ({
|
|||
offset,
|
||||
timestamp,
|
||||
content,
|
||||
}) => {
|
||||
const copyData = () => {
|
||||
if (navigator.clipboard)
|
||||
navigator.clipboard.writeText(JSON.stringify(content || {}));
|
||||
};
|
||||
const saveFile = () => {
|
||||
let extension = 'json';
|
||||
if (typeof content === 'string') {
|
||||
try {
|
||||
JSON.parse(content);
|
||||
} catch (e) {
|
||||
extension = 'txt';
|
||||
}
|
||||
}
|
||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify(content || {})
|
||||
)}`;
|
||||
const downloadAnchorNode = document.createElement('a');
|
||||
downloadAnchorNode.setAttribute('href', dataStr);
|
||||
downloadAnchorNode.setAttribute(
|
||||
'download',
|
||||
`topic-message[${timestamp}].${extension}`
|
||||
);
|
||||
document.body.appendChild(downloadAnchorNode);
|
||||
downloadAnchorNode.click();
|
||||
downloadAnchorNode.remove();
|
||||
};
|
||||
|
||||
const buttonStyle = {
|
||||
height: '30px',
|
||||
};
|
||||
const buttonClasses = 'button is-link is-outlined topic-message-button';
|
||||
return (
|
||||
<tr>
|
||||
<td style={{ width: 200 }}>{format(timestamp, 'yyyy-MM-dd HH:mm:ss')}</td>
|
||||
<td style={{ width: 150 }}>{offset}</td>
|
||||
<td style={{ width: 100 }}>{partition}</td>
|
||||
<td style={{ wordBreak: 'break-word' }}>
|
||||
{content && (
|
||||
<div>
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<button
|
||||
className={buttonClasses}
|
||||
data-title="Copy the message to the clipboard"
|
||||
type="button"
|
||||
style={{ ...buttonStyle, marginRight: '5px' }}
|
||||
onClick={copyData}
|
||||
>
|
||||
<i className="far fa-clipboard" />
|
||||
</button>
|
||||
<button
|
||||
className={buttonClasses}
|
||||
data-title="Download the message as a .json/.txt file"
|
||||
type="button"
|
||||
style={buttonStyle}
|
||||
onClick={saveFile}
|
||||
>
|
||||
<i className="fas fa-file-download" />
|
||||
</button>
|
||||
</div>
|
||||
<JSONViewer data={content as { [key: string]: string }} />
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
}) => (
|
||||
<tr>
|
||||
<td style={{ width: 200 }}>{format(timestamp, 'yyyy-MM-dd HH:mm:ss')}</td>
|
||||
<td style={{ width: 150 }}>{offset}</td>
|
||||
<td style={{ width: 100 }}>{partition}</td>
|
||||
<td style={{ wordBreak: 'break-word' }}>
|
||||
{content && (
|
||||
<div>
|
||||
<JSONViewer data={content as { [key: string]: string }} />
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
export default MessageItem;
|
||||
|
|
|
@ -37,46 +37,6 @@ exports[`MessageItem when content is defined matches snapshot 1`] = `
|
|||
}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<button
|
||||
className="button is-link is-outlined topic-message-button"
|
||||
data-title="Copy the message to the clipboard"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": "30px",
|
||||
"marginRight": "5px",
|
||||
}
|
||||
}
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
className="far fa-clipboard"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className="button is-link is-outlined topic-message-button"
|
||||
data-title="Download the message as a .json/.txt file"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": "30px",
|
||||
}
|
||||
}
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
className="fas fa-file-download"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<JSONViewer
|
||||
data={
|
||||
Object {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
|
||||
interface ButtonProps {
|
||||
callback: () => void;
|
||||
classes?: string;
|
||||
title: string;
|
||||
style?: { [key: string]: string | number };
|
||||
text: {
|
||||
default: string;
|
||||
dynamic: string;
|
||||
};
|
||||
}
|
||||
|
||||
const DynamicButton: React.FC<ButtonProps> = ({
|
||||
callback,
|
||||
classes,
|
||||
title,
|
||||
style,
|
||||
text,
|
||||
children,
|
||||
}) => {
|
||||
const [buttonText, setButtonText] = React.useState(text.default);
|
||||
let timeout: number;
|
||||
const clickHandler = () => {
|
||||
callback();
|
||||
setButtonText(text.dynamic);
|
||||
timeout = window.setTimeout(() => setButtonText(text.default), 3000);
|
||||
};
|
||||
React.useEffect(() => () => window.clearTimeout(timeout), [callback]);
|
||||
return (
|
||||
<button
|
||||
className={classes}
|
||||
title={title}
|
||||
type="button"
|
||||
style={style}
|
||||
onClick={clickHandler}
|
||||
>
|
||||
{children}
|
||||
<span>{buttonText}</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicButton;
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import JSONTree from 'react-json-tree';
|
||||
import useDataSaver from 'lib/hooks/useDataSaver';
|
||||
import theme from './themes/google';
|
||||
import DynamicButton from './DynamicButton';
|
||||
|
||||
interface JSONViewerProps {
|
||||
data: {
|
||||
|
@ -8,8 +10,51 @@ interface JSONViewerProps {
|
|||
};
|
||||
}
|
||||
|
||||
const JSONViewer: React.FC<JSONViewerProps> = ({ data }) => (
|
||||
<JSONTree data={data} theme={theme} shouldExpandNode={() => true} hideRoot />
|
||||
);
|
||||
const JSONViewer: React.FC<JSONViewerProps> = ({ data }) => {
|
||||
const { copyToClipboard, saveFile } = useDataSaver();
|
||||
const copyButtonHandler = () => {
|
||||
copyToClipboard(JSON.stringify(data));
|
||||
};
|
||||
const buttonClasses = 'button is-link is-outlined is-small is-centered';
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="field has-addons"
|
||||
style={{
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<DynamicButton
|
||||
callback={copyButtonHandler}
|
||||
classes={buttonClasses}
|
||||
title="Copy the message to the clipboard"
|
||||
style={{ marginRight: '5px' }}
|
||||
text={{ default: 'Copy', dynamic: 'Copied!' }}
|
||||
>
|
||||
<span className="icon">
|
||||
<i className="far fa-clipboard" />
|
||||
</span>
|
||||
</DynamicButton>
|
||||
<button
|
||||
className={buttonClasses}
|
||||
title="Download the message as a .json/.txt file"
|
||||
type="button"
|
||||
onClick={() => saveFile(JSON.stringify(data), `topic-message`)}
|
||||
>
|
||||
<span className="icon">
|
||||
<i className="fas fa-file-download" />
|
||||
</span>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
</div>
|
||||
<JSONTree
|
||||
data={data}
|
||||
theme={theme}
|
||||
shouldExpandNode={() => true}
|
||||
hideRoot
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default JSONViewer;
|
||||
|
|
27
kafka-ui-react-app/src/lib/hooks/useDataSaver.tsx
Normal file
27
kafka-ui-react-app/src/lib/hooks/useDataSaver.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
const useDataSaver = () => {
|
||||
const copyToClipboard = (content: string) => {
|
||||
if (navigator.clipboard) navigator.clipboard.writeText(content);
|
||||
};
|
||||
|
||||
const saveFile = (content: string, fileName: string) => {
|
||||
let extension = 'json';
|
||||
try {
|
||||
JSON.parse(content);
|
||||
} catch (e) {
|
||||
extension = 'txt';
|
||||
}
|
||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
|
||||
content
|
||||
)}`;
|
||||
const downloadAnchorNode = document.createElement('a');
|
||||
downloadAnchorNode.setAttribute('href', dataStr);
|
||||
downloadAnchorNode.setAttribute('download', `${fileName}.${extension}`);
|
||||
document.body.appendChild(downloadAnchorNode);
|
||||
downloadAnchorNode.click();
|
||||
downloadAnchorNode.remove();
|
||||
};
|
||||
|
||||
return { copyToClipboard, saveFile };
|
||||
};
|
||||
|
||||
export default useDataSaver;
|
Loading…
Add table
Reference in a new issue