Skip to content

Commit 1ecd22b

Browse files
author
Dylan Zhang
committed
Updated terminal with extra features
- changed actual comparisons using === to use .includes() instead for clearner comparisons - made cursor stay on terminal if redirecting using terminal
1 parent 07c42b2 commit 1ecd22b

File tree

1 file changed

+70
-53
lines changed

1 file changed

+70
-53
lines changed

frontend/src/components/Terminal.tsx

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,117 @@
11
import { useRouter } from "next/router";
2-
import { useRef, useState } from "react";
2+
import { useEffect, useRef, useState } from "react";
33

44
const Terminal = () => {
5-
// Store the value of the input
65
const [value, setValue] = useState("");
6+
const [inputFocused, setInputFocused] = useState(false);
77

8-
// Automatically select the end of the input as the custom
9-
// cursor only works at the end of the input.
108
const inputRef = useRef<HTMLInputElement>(null);
9+
const router = useRouter();
10+
11+
// Automatically focus and move cursor to the end
1112
const setInputEnd = () => {
1213
if (inputRef.current) {
1314
const len = inputRef.current.value.length;
1415
inputRef.current.setSelectionRange(len, len);
1516
}
16-
}
17+
};
1718

18-
// Keep track of if the input is focused
19-
const [inputFocused, setInputFocused] = useState(false);
19+
// Use localStorage to keep focus on the terminal if redirecting using terminal
20+
useEffect(() => {
21+
if (localStorage.getItem("fromTerminal") === "true") {
22+
localStorage.removeItem("fromTerminal");
23+
if (inputRef.current) {
24+
inputRef.current.focus();
25+
setInputEnd();
26+
setInputFocused(true);
27+
}
28+
}
29+
}, []);
2030

21-
// Using the router to change pages seamlessly
22-
const router = useRouter();
2331
const goToPage = (target: string) => {
32+
localStorage.setItem("fromTerminal", "true");
2433
router.push(target);
2534
};
26-
27-
// Checking for "Enter" and if so, changing to
28-
// the inputted page
35+
2936
const handleKey = (key: string) => {
3037
if (key !== "Enter") return;
3138

32-
if (value.toLowerCase() === "~"
33-
|| value.toLowerCase() === "cd"
34-
|| value.toLowerCase() === "cd ~"
35-
|| value.toLowerCase() === "cd .."
36-
) {
39+
const cmd = value.toLowerCase().trim();
40+
41+
if (["~", "cd", "cd ~", "cd .."].includes(cmd)) {
3742
goToPage("/");
38-
} else if (value.toLowerCase() === "cd about"
39-
|| value.toLowerCase() === "cd about us"
40-
|| value.toLowerCase() === "cd about_us"
41-
) {
43+
} else if (["cd about", "cd about us", "cd about_us"].includes(cmd)) {
4244
goToPage("/about");
43-
} else if (value.toLowerCase() === "cd events"
44-
|| value.toLowerCase() === "cd event"
45-
) {
45+
} else if (["cd events", "cd event"].includes(cmd)) {
4646
goToPage("/events");
47-
} else if (value.toLowerCase() === "cd resources"
48-
|| value.toLowerCase() === "cd resource"
49-
) {
47+
} else if (["cd resources", "cd resource"].includes(cmd)) {
5048
goToPage("/resources");
51-
} else if (value.toLowerCase() === "cd sponsors"
52-
|| value.toLowerCase() === "cd sponsor"
53-
) {
49+
} else if (["cd sponsors", "cd sponsor"].includes(cmd)) {
5450
goToPage("/sponsors");
55-
} else if (value.toLowerCase() === "cd contact"
56-
|| value.toLowerCase() === "cd contacts"
57-
|| value.toLowerCase() === "cd contact us"
58-
|| value.toLowerCase() === "cd contact_us"
59-
) {
51+
} else if (["cd contact", "cd contacts", "cd contact us", "cd contact_us"].includes(cmd)) {
6052
goToPage("/contact-us");
53+
} else if (cmd === "cd constitution") {
54+
goToPage("/about/constitution");
55+
} else if (
56+
["cd execs", "cd directors", "cd subcom", "cd execs directors subcom", "cd execs-directors-subcom", "cd execs_directors_subcom"].includes(cmd)
57+
) {
58+
goToPage("/about/execs-directors-subcom");
59+
} else if (
60+
["history", "cd our history", "cd our-history", "cd our_history"].includes(cmd)
61+
) {
62+
goToPage("/about/our-history");
63+
} else if (
64+
["cd faq", "cd faqs", "cd questions", "cd frequently asked questions"].includes(cmd)
65+
) {
66+
goToPage("/about/faqs");
67+
} else if (
68+
["cd election-guide", "cd election guide", "cd election"].includes(cmd)
69+
) {
70+
goToPage("/about/election-guide");
6171
}
6272

63-
clearInput()
73+
clearInput();
6474
};
6575

6676
const clearInput = () => {
6777
setValue("");
6878
};
6979

7080
return (
71-
// Using relative + absolute to overlap the `input` and `span`
7281
<span className="relative">
73-
{/* The input */}
74-
<input type="text" id="input" value={value} ref={inputRef} maxLength={40}
82+
<input
83+
type="text"
84+
id="input"
85+
value={value}
86+
ref={inputRef}
87+
maxLength={40}
7588
className="absolute text-blue-500 p-0 m-0 bg-transparent outline-none caret-transparent w-[50vw] z-10"
7689
onKeyDown={(e) => {
77-
handleKey(e.key)
78-
setInputEnd()
90+
handleKey(e.key);
91+
setInputEnd();
7992
}}
8093
onChange={(e) => setValue(e.target.value)}
8194
onFocus={() => setInputFocused(true)}
8295
onBlur={() => {
83-
clearInput()
84-
setInputFocused(false)
96+
clearInput();
97+
setInputFocused(false);
8598
}}
86-
></input>
87-
{/* The custom cursor */}
99+
/>
88100
<span className="absolute w-[60vw] p-0 m-0 z-0">
89-
{/* The invisable span that is the same length as the input */}
101+
<span className="invisible whitespace-pre pointer-events-none text-base">
102+
{value}
103+
</span>
90104
<span
91-
className="invisible whitespace-pre pointer-events-none text-base"
92-
>{value}</span>
93-
{/* The custom cursor */}
94-
<span id="cursor" className={`text-${inputFocused ? "white" : "gray-500"} pointer-events-none inline-block animate-blink p-0 m-0`}>_</span>
105+
id="cursor"
106+
className={`text-${
107+
inputFocused ? "white" : "gray-500"
108+
} pointer-events-none inline-block animate-blink p-0 m-0`}
109+
>
110+
_
111+
</span>
95112
</span>
96113
</span>
97-
)
98-
}
114+
);
115+
};
99116

100-
export default Terminal
117+
export default Terminal;

0 commit comments

Comments
 (0)