Skip to content

Commit 5942207

Browse files
authored
Merge pull request #50 from csesoc/I2-15/interactable-terminal
Added interactable terminal
2 parents bec7ffb + 07c42b2 commit 5942207

File tree

4 files changed

+115
-12
lines changed

4 files changed

+115
-12
lines changed

frontend/package-lock.json

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/components/Navbar.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Image from 'next/image';
22
import Link from 'next/link';
33
import { useEffect, useState } from 'react';
44
import Hamburger from './Hamburger';
5+
import Terminal from './Terminal';
56

67
const Navbar = () => {
78
const [path, setPath] = useState<string[]>([]);
@@ -13,22 +14,25 @@ const Navbar = () => {
1314

1415
return (
1516
<nav className="sticky top-0 flex justify-between items-center relative z-10 shadow-lg rounded-md bg-black/15 backdrop-blur-md xl:px-24 md:px-10 px-5 py-6">
16-
<Link href="/">
17-
<Image
18-
src="/assets/csesoc_logo.svg"
19-
alt="CSESoc Logo"
20-
width={200}
21-
height={200}
22-
draggable={false}
23-
/>
17+
<div>
18+
<Link href="/">
19+
<Image
20+
src="/assets/csesoc_logo.svg"
21+
alt="CSESoc Logo"
22+
width={200}
23+
height={200}
24+
draggable={false}
25+
/>
26+
</Link>
2427
<p className="font-mono mt-3 font-bold">
2528
<span className="text-green-500">csesoc@unsw</span>
2629
<span>:</span>
2730
<span className="text-blue-500">~{path.map(segment => '/' + segment.toLowerCase())}</span>
2831
<span>$ </span>
29-
<span id="cursor" className="text-gray-400 inline-block animate-blink">_</span>
32+
{/* The interactive terminal that allows changing pages using 'cd' */}
33+
<Terminal/>
3034
</p>
31-
</Link>
35+
</div>
3236
<div>
3337
<div className="md:flex xl:gap-18 lg:gap-10 md:gap-5 text-right font-bold hidden">
3438
<Link href="/about">

frontend/src/components/Terminal.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useRouter } from "next/router";
2+
import { useRef, useState } from "react";
3+
4+
const Terminal = () => {
5+
// Store the value of the input
6+
const [value, setValue] = useState("");
7+
8+
// Automatically select the end of the input as the custom
9+
// cursor only works at the end of the input.
10+
const inputRef = useRef<HTMLInputElement>(null);
11+
const setInputEnd = () => {
12+
if (inputRef.current) {
13+
const len = inputRef.current.value.length;
14+
inputRef.current.setSelectionRange(len, len);
15+
}
16+
}
17+
18+
// Keep track of if the input is focused
19+
const [inputFocused, setInputFocused] = useState(false);
20+
21+
// Using the router to change pages seamlessly
22+
const router = useRouter();
23+
const goToPage = (target: string) => {
24+
router.push(target);
25+
};
26+
27+
// Checking for "Enter" and if so, changing to
28+
// the inputted page
29+
const handleKey = (key: string) => {
30+
if (key !== "Enter") return;
31+
32+
if (value.toLowerCase() === "~"
33+
|| value.toLowerCase() === "cd"
34+
|| value.toLowerCase() === "cd ~"
35+
|| value.toLowerCase() === "cd .."
36+
) {
37+
goToPage("/");
38+
} else if (value.toLowerCase() === "cd about"
39+
|| value.toLowerCase() === "cd about us"
40+
|| value.toLowerCase() === "cd about_us"
41+
) {
42+
goToPage("/about");
43+
} else if (value.toLowerCase() === "cd events"
44+
|| value.toLowerCase() === "cd event"
45+
) {
46+
goToPage("/events");
47+
} else if (value.toLowerCase() === "cd resources"
48+
|| value.toLowerCase() === "cd resource"
49+
) {
50+
goToPage("/resources");
51+
} else if (value.toLowerCase() === "cd sponsors"
52+
|| value.toLowerCase() === "cd sponsor"
53+
) {
54+
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+
) {
60+
goToPage("/contact-us");
61+
}
62+
63+
clearInput()
64+
};
65+
66+
const clearInput = () => {
67+
setValue("");
68+
};
69+
70+
return (
71+
// Using relative + absolute to overlap the `input` and `span`
72+
<span className="relative">
73+
{/* The input */}
74+
<input type="text" id="input" value={value} ref={inputRef} maxLength={40}
75+
className="absolute text-blue-500 p-0 m-0 bg-transparent outline-none caret-transparent w-[50vw] z-10"
76+
onKeyDown={(e) => {
77+
handleKey(e.key)
78+
setInputEnd()
79+
}}
80+
onChange={(e) => setValue(e.target.value)}
81+
onFocus={() => setInputFocused(true)}
82+
onBlur={() => {
83+
clearInput()
84+
setInputFocused(false)
85+
}}
86+
></input>
87+
{/* The custom cursor */}
88+
<span className="absolute w-[60vw] p-0 m-0 z-0">
89+
{/* The invisable span that is the same length as the input */}
90+
<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>
95+
</span>
96+
</span>
97+
)
98+
}
99+
100+
export default Terminal

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)