Skip to content

Commit

Permalink
add interactive terminal component w/ json view
Browse files Browse the repository at this point in the history
  • Loading branch information
cordt-sei committed Oct 24, 2024
1 parent 31ab3ab commit 5b7a652
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
59 changes: 59 additions & 0 deletions components/InteractiveTerminal/InteractiveTerminal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useEffect, useState } from 'react';
import styles from '../../styles/InteractiveTerminal.module.css';

const InteractiveTerminal: React.FC = () => {
const [jsonData, setJsonData] = useState<any>(null);
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(new Set());

useEffect(() => {
fetch('https://raw.githubusercontent.com/cosmos/chain-registry/refs/heads/master/sei/chain.json')
.then((res) => res.json())
.then((data) => setJsonData(data))
.catch((err) => console.error('Failed to fetch JSON data:', err));
}, []);

const toggleNode = (path: string) => {
setExpandedNodes((prevNodes) => {
const newNodes = new Set(prevNodes);
if (newNodes.has(path)) {
newNodes.delete(path);
} else {
newNodes.add(path);
}
return newNodes;
});
};

const renderNode = (key: string, value: any, path: string) => {
const isExpandable = typeof value === 'object' && value !== null;
const isOpen = expandedNodes.has(path);
const displayKey = key || 'root';

return (
<div key={path} className={styles.node}>
<div className={styles.nodeHeader} onClick={() => isExpandable && toggleNode(path)}>
{isExpandable && (isOpen ? '▼' : '▶')} {displayKey}
</div>
{isExpandable && isOpen && (
<div className={styles.nodeChildren}>
{Object.entries(value).map(([childKey, childValue]) => renderNode(childKey, childValue, `${path}.${childKey}`))}
</div>
)}
{!isExpandable && <div className={styles.leafNode}>{String(value)}</div>}
</div>
);
};

return (
<div className={styles.terminal}>
<div className={styles.header}>
<div className={styles.circle} style={{ backgroundColor: '#FF605C' }} />
<div className={styles.circle} style={{ backgroundColor: '#FFBD44' }} />
<div className={styles.circle} style={{ backgroundColor: '#00CA4E' }} />
</div>
<div className={styles.body}>{jsonData ? renderNode('', jsonData, 'root') : <div className={styles.loading}>Loading...</div>}</div>
</div>
);
};

export default InteractiveTerminal;
1 change: 1 addition & 0 deletions components/InteractiveTerminal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InteractiveTerminal } from './InteractiveTerminal';
1 change: 1 addition & 0 deletions components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from './Nfts';
export * from './VersionFetcher';
export * from './PropertyInfo';
export * from './EcosystemMap';
export * from './InteractiveTerminal';
53 changes: 53 additions & 0 deletions styles/InteractiveTerminal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.terminal {
background-color: #1e1e1e;
color: #00ff00;
border-radius: 8px;
padding: 16px;
font-family: 'Courier New', monospace;
width: 600px;
margin: 0 auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.header {
display: flex;
justify-content: flex-start;
padding-bottom: 8px;
}

.circle {
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
}

.body {
background-color: #1b1b1b;
border-radius: 4px;
padding: 8px;
overflow-y: auto;
max-height: 400px;
}

.node {
margin-left: 16px;
}

.nodeHeader {
cursor: pointer;
user-select: none;
}

.nodeChildren {
margin-left: 16px;
}

.leafNode {
margin-left: 32px;
}

.loading {
text-align: center;
color: #d1d5db;
}

0 comments on commit 5b7a652

Please sign in to comment.