react-hackterm
Version:
Hackable terminal emulator in React
243 lines (213 loc) • 6.51 kB
JavaScript
import React from "react";
import PropTypes from "prop-types";
import cookie from "cookie_js";
import Command from "./Command";
import Prefix from "./Prefix";
import Bar from "./Bar";
import Theme from "./themes";
class Terminal extends React.Component {
constructor(props) {
super(props);
this.state = {
terminalHistory: "",
commands: [
{
name: "info",
description: "Get terminal information",
value: "Hackable terminal emulator in React"
},
{
name: "repo",
description: "Get terminal repo",
value: "https://github.com/kimolalekan/react-hackterm.git"
},
{
name: "date",
description: "Get date",
value: new Date()
}
]
};
}
goToBottom() {
document.querySelector("#terminal-editor").focus();
document.querySelector("#terminal-editor").scrollIntoView();
}
help(config) {
let val = "";
this.state.commands.forEach((item, key) => {
let command =
"<div><span class='command'>" +
item.name +
"</span> - " +
item.description +
"</div>";
val += command;
});
let input = document.querySelector("#terminal-editor").value;
input = input.trim();
if (input === "help") {
document.querySelector(".output").innerHTML +=
"<div style='margin-bottom: 20px; font-family: " +
config.font +
"; font-size: " +
config.text +
"px;'>Available commands: <div style='margin-top: 10px'>" +
val +
"</div></div>";
document.querySelector("#terminal-editor").value = "";
this.goToBottom();
}
}
clear() {
let input = document.querySelector("#terminal-editor").value;
input = input.trim();
if (input === "clear") {
document.querySelector(".output").innerHTML = "";
document.querySelector("#terminal-editor").value = "";
this.goToBottom();
}
}
noCommand(config) {
let input = document.querySelector("#terminal-editor").value;
input = input.trim();
if (input.length >= 1 && input !== "help" && input !== "clear") {
document.querySelector(".output").innerHTML +=
"<div style='margin-bottom: 20px; font-family: " +
config.font +
"; font-size: " +
config.text +
"px;'><span class='command'>" +
input +
"</span>: command not found</div>";
document.querySelector("#terminal-editor").value = "";
this.goToBottom();
}
}
emptyCommand(config) {
let input = document.querySelector("#terminal-editor").value;
input = input.trim();
if (input.length < 1) {
document.querySelector(".output").innerHTML +=
"<div style='width:100%; display: block;'>" + Prefix(config) + "</div>";
document.querySelector("#terminal-editor").value = "";
this.goToBottom();
}
}
realCommand() {
let input = document.querySelector("#terminal-editor").value;
input = input.trim();
this.state.commands.forEach(item => {
if (item.name === input) {
document.querySelector(".output").innerHTML +=
"<div style='margin-bottom: 20px'>" +
Command(this.props.config, item.name, item.value) +
"</div>";
document.querySelector("#terminal-editor").value = "";
this.goToBottom();
}
});
}
navigateHistory(e) {
e = e || window.event;
if (e.keyCode === 38) {
// up arrow
console.log("GOING UP");
} else if (e.keyCode === 40) {
// down arrow
console.log("GOING DOWN");
}
}
handleTerminal(e) {
e.preventDefault();
let input = document.querySelector("#terminal-editor").value;
let val = [input];
let history = cookie.get("terminal_history");
history = history ? JSON.parse(history) : [];
history = [...val, ...history];
history = JSON.stringify(history);
cookie.set("terminal_history", history);
//If there is empty command return empty value
this.emptyCommand(this.props.config);
//Offer help, if command is called
this.help(this.props.config);
//Real commands
this.realCommand();
//If there is no command return default
this.noCommand(this.props.config);
//Clear first
this.clear();
}
loadCommands() {
let commands = this.state.commands;
let command = this.props.command ? this.props.command : [];
commands = [...commands, ...command];
this.setState({ commands });
}
componentDidMount() {
this.loadCommands();
}
render() {
const { bar, config, terminalHistory, theme } = this.props;
return (
<div
className="terminal-theme"
style={{ borderRadius: config.edge ? 5 : 0, width: config.width }}
>
<Theme value={theme} />
<Bar type={bar} size={config.width} />
<div
id="terminal"
className="terminal"
style={{
width: config.width,
height: config.height,
borderRadius: config.edge ? 5 : 0
}}
>
<div className="inner">
<div className="output" />
<form className="editor" onSubmit={this.handleTerminal.bind(this)}>
<span
className="prefix"
style={{
fontFamily: `${config.font}`,
fontSize: config.text
}}
>
{config.mode === "default" ? "$" : ""}
{config.mode === "root" && config.modeText
? `root@${config.modeText} #`
: ""}
{config.mode === "custom" && config.modeText ? (
<span>❯</span>
) : (
""
)}
</span>
<input
id="terminal-editor"
className="input"
autoComplete="off"
spellCheck={false}
value={terminalHistory}
style={{
fontFamily: `${config.font}`,
fontSize: config.text
}}
onKeyUp={this.navigateHistory.bind(this)}
/>
</form>
</div>
</div>
</div>
);
}
}
Terminal.propTypes = {
bar: PropTypes.string,
config: PropTypes.object.isRequired,
command: PropTypes.array,
theme: PropTypes.string
};
export default Terminal;