diff --git a/src/components/Terminal.tsx b/src/components/Terminal.tsx index 3b36b6e..b2046ca 100644 --- a/src/components/Terminal.tsx +++ b/src/components/Terminal.tsx @@ -1,20 +1,62 @@ -import { type FunctionComponent, type PropsWithChildren } from "react"; +import { useRef, useState, useEffect, type ReactNode } from "react"; import clsx from "clsx"; +import { TerminalContextProvider } from "~/context/TerminalContext"; -type TerminalProps = PropsWithChildren<{ +export const Terminal = (props: { + children?: ReactNode; className?: string; -}>; +}) => { + const terminalRef = useRef(null); -export const Terminal: FunctionComponent = ({ - children, - className, -}) => ( -
- {children} -
-); + const [size, setSize] = useState<{ cols: number; rows: number }>(); + + useEffect(() => { + const precision = 300; + + const calculateSize = () => { + if (!terminalRef.current) return; + + const node = document.createElement("span"); + node.style.color = "transparent"; + node.style.position = "absolute"; + node.textContent = "A".repeat(precision); + + terminalRef.current.appendChild(node); + + setSize({ + cols: Math.floor( + (terminalRef.current.offsetWidth - 4) / + (node.offsetWidth / precision), + ), + rows: Math.floor( + (terminalRef.current.offsetHeight - 4) / node.offsetHeight, + ), + }); + + node.remove(); + }; + + calculateSize(); + + window.addEventListener("resize", calculateSize); + + return () => { + window.removeEventListener("resize", calculateSize); + }; + }, []); + + return ( + +
+ {size && props.children} +
+
+ ); +}; diff --git a/src/context/TerminalContext.tsx b/src/context/TerminalContext.tsx new file mode 100644 index 0000000..fc5ba9c --- /dev/null +++ b/src/context/TerminalContext.tsx @@ -0,0 +1,15 @@ +import { createContext, useContext } from "react"; + +const TerminalContext = createContext<{ cols: number; height: number } | null>( + null, +); + +export const TerminalContextProvider = TerminalContext.Provider; + +export const useTerminal = () => { + const context = useContext(TerminalContext); + if (!context) + throw new Error("useTerminal must be used inside a Terminal component"); + + return context; +};