feat(terminal): terminal wrapper and context
This commit is contained in:
@@ -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<HTMLDivElement>(null);
|
||||
|
||||
export const Terminal: FunctionComponent<TerminalProps> = ({
|
||||
children,
|
||||
className,
|
||||
}) => (
|
||||
<div
|
||||
className={clsx(
|
||||
"rounded-lg border-2 border-[#595959] bg-[#1e1e2e] bg-opacity-95 px-1 text-[#cdd6f4] shadow-window transition-colors duration-[500ms] ease-out hover:border-[#cdd6f4] hover:duration-[200ms]",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
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 (
|
||||
<TerminalContextProvider value={size}>
|
||||
<div
|
||||
ref={terminalRef}
|
||||
className={clsx(
|
||||
"overflow-hidden rounded-lg border-2 border-borderInactive bg-background bg-opacity-80 text-lg text-color7 text-foreground shadow-window transition-colors duration-[500ms] ease-out hover:border-borderActive hover:duration-[200ms]",
|
||||
props.className,
|
||||
)}
|
||||
style={{ backdropFilter: "blur(2px)" }}
|
||||
>
|
||||
{size && props.children}
|
||||
</div>
|
||||
</TerminalContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
15
src/context/TerminalContext.tsx
Normal file
15
src/context/TerminalContext.tsx
Normal file
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user