diff --git a/src/components/applications/terminal/InputLine.jsx b/src/components/applications/terminal/InputLine.jsx
new file mode 100644
index 0000000..e3f300f
--- /dev/null
+++ b/src/components/applications/terminal/InputLine.jsx
@@ -0,0 +1,53 @@
+import { useState } from "react";
+import styles from "./Terminal.module.css";
+
+/**
+ * @param {object} props
+ * @param {string} props.value
+ * @param {string} props.prefix
+ * @param {Function} props.onChange
+ * @param {Function} props.onKeyUp
+ * @param {Function} props.onKeyDown
+ * @param {import("react").MutableRefObject} props.inputRef
+ */
+export function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRef }) {
+ const [cursorPosition, setCursorPosition] = useState(0);
+
+ const checkCursorPosition = () => {
+ setCursorPosition(inputRef.current?.selectionStart);
+ };
+
+ return (
+
+ {prefix && {prefix}
}
+
+ {value}
+ {
+ onChange(event);
+ checkCursorPosition();
+ }}
+ ref={inputRef}
+ onKeyUp={onKeyUp}
+ onKeyDown={(event) => {
+ onKeyDown(event);
+ checkCursorPosition();
+ }}
+ onClick={checkCursorPosition}
+ onTouchEnd={checkCursorPosition}
+ onSelect={checkCursorPosition}
+ onCut={checkCursorPosition}
+ onCopy={checkCursorPosition}
+ onPaste={checkCursorPosition}
+ spellCheck={false}
+ autoComplete="off"
+ autoFocus
+ size=""
+ />
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/applications/terminal/OutputLine.jsx b/src/components/applications/terminal/OutputLine.jsx
new file mode 100644
index 0000000..08d78fe
--- /dev/null
+++ b/src/components/applications/terminal/OutputLine.jsx
@@ -0,0 +1,15 @@
+import styles from "./Terminal.module.css";
+
+/**
+ * @param {object} props
+ * @param {string} props.text
+ */
+export function OutputLine({ text }) {
+ const lines = text?.split("\n");
+
+ return (<>
+ {lines.map((line, index) =>
+
{line}
+ )}
+ >);
+}
\ No newline at end of file
diff --git a/src/components/applications/terminal/Terminal.jsx b/src/components/applications/terminal/Terminal.jsx
index fac2cb9..5d4c443 100644
--- a/src/components/applications/terminal/Terminal.jsx
+++ b/src/components/applications/terminal/Terminal.jsx
@@ -3,75 +3,12 @@ import styles from "./Terminal.module.css";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js";
import { Command } from "../../../features/applications/terminal/commands.js";
import { clamp } from "../../../features/math/clamp.js";
+import { OutputLine } from "./OutputLine.jsx";
+import { InputLine } from "./InputLine.jsx";
const USERNAME = "user";
const HOSTNAME = "prozilla-os";
-/**
- * @param {object} props
- * @param {string} props.text
- */
-function OutputLine({ text }) {
- const lines = text?.split("\n");
-
- return (<>
- {lines.map((line, index) =>
- {line}
- )}
- >);
-}
-
-/**
- * @param {object} props
- * @param {string} props.value
- * @param {string} props.prefix
- * @param {Function} props.onChange
- * @param {Function} props.onKeyUp
- * @param {Function} props.onKeyDown
- * @param {import("react").MutableRefObject} props.inputRef
- */
-function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRef }) {
- const [cursorPosition, setCursorPosition] = useState(0);
-
- const checkCursorPosition = () => {
- setCursorPosition(inputRef.current?.selectionStart);
- };
-
- return (
-
- {prefix && {prefix}
}
-
- {value}
- {
- onChange(event);
- checkCursorPosition();
- }}
- ref={inputRef}
- onKeyUp={onKeyUp}
- onKeyDown={(event) => {
- onKeyDown(event);
- checkCursorPosition();
- }}
- onClick={checkCursorPosition}
- onTouchEnd={checkCursorPosition}
- onSelect={checkCursorPosition}
- onCut={checkCursorPosition}
- onCopy={checkCursorPosition}
- onPaste={checkCursorPosition}
- spellCheck={false}
- autoComplete="off"
- autoFocus
- size=""
- />
-
-
- );
-}
-
export function Terminal({ setTitle }) {
const [inputKey, setInputKey] = useState(0);
const [inputValue, setInputValue] = useState("");
diff --git a/src/components/desktop/Desktop.jsx b/src/components/desktop/Desktop.jsx
index 1d65d46..3971169 100644
--- a/src/components/desktop/Desktop.jsx
+++ b/src/components/desktop/Desktop.jsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import { memo, useState } from "react";
import { SettingsManager } from "../../features/settings/settingsManager.js";
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext.js";
import styles from "./Desktop.module.css";
@@ -8,7 +8,7 @@ import { ModalsView } from "../modals/ModalsView.jsx";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
-export function Desktop() {
+export const Desktop = memo(() => {
const settingsManager = useSettingsManager();
const [wallpaper, setWallpaper] = useState(null);
const [modalsManager, modals] = useModals();
@@ -37,4 +37,4 @@ export function Desktop() {
>);
-}
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/src/components/modals/ModalView.jsx b/src/components/modals/ModalView.jsx
index db21615..a427fea 100644
--- a/src/components/modals/ModalView.jsx
+++ b/src/components/modals/ModalView.jsx
@@ -1,3 +1,4 @@
+import { memo } from "react";
import { Modal as ModalType } from "../../features/modals/modal.js";
import OutsideClickListener from "../../hooks/utils/outsideClick.js";
import styles from "./ModalView.module.css";
@@ -6,7 +7,7 @@ import styles from "./ModalView.module.css";
* @param {object} root
* @param {ModalType} root.modal
*/
-export function ModalView({ modal }) {
+export const ModalView = memo(({ modal }) => {
return (
{ modal.close(); }}>
@@ -14,4 +15,4 @@ export function ModalView({ modal }) {
);
-}
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/src/components/modals/ModalsView.jsx b/src/components/modals/ModalsView.jsx
index 5285dc9..88117cf 100644
--- a/src/components/modals/ModalsView.jsx
+++ b/src/components/modals/ModalsView.jsx
@@ -1,3 +1,4 @@
+import { memo } from "react";
import { Modal } from "../../features/modals/modal.js";
import ModalsManager from "../../features/modals/modals.js";
import { ModalView } from "./ModalView.jsx";
@@ -10,7 +11,7 @@ import styles from "./ModalsView.module.css";
* @param {import("react").CSSProperties} root.style
* @param {import("react").className} root.className
*/
-export function ModalsView({ modalsManager, modals, style, className }) {
+export const ModalsView = memo(({ modalsManager, modals, style, className }) => {
return (
{modals?.map((modal) =>
@@ -18,4 +19,4 @@ export function ModalsView({ modalsManager, modals, style, className }) {
)}
);
-}
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/src/components/task-bar/AppButton.jsx b/src/components/task-bar/AppButton.jsx
new file mode 100644
index 0000000..6d1da2b
--- /dev/null
+++ b/src/components/task-bar/AppButton.jsx
@@ -0,0 +1,36 @@
+import { memo, useEffect, useState } from "react";
+import Application from "../../features/applications/application.js";
+import { useWindows } from "../../hooks/windows/windowsContext.js";
+import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
+import styles from "./TaskBar.module.css";
+import { ReactSVG } from "react-svg";
+
+/**
+ * @param {object} props
+ * @param {Application} props.app
+ */
+export const AppButton = memo(({ app }) => {
+ const [active, setActive] = useState(false);
+ const windows = useWindows();
+ const windowsManager = useWindowsManager();
+
+ useEffect(() => {
+ setActive(windowsManager.isAppActive(app.id));
+ }, [app.id, windows, windowsManager]);
+
+ const classNames = [styles["App-icon"]];
+ if (active)
+ classNames.push(styles.Active);
+
+ return (
+
+ );
+});
\ No newline at end of file
diff --git a/src/components/task-bar/TaskBar.jsx b/src/components/task-bar/TaskBar.jsx
index 3555d83..b625a01 100644
--- a/src/components/task-bar/TaskBar.jsx
+++ b/src/components/task-bar/TaskBar.jsx
@@ -1,12 +1,9 @@
-import { useEffect, useRef, useState } from "react";
+import { memo, useRef, useState } from "react";
import styles from "./TaskBar.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import ApplicationsManager from "../../features/applications/applications.js";
-import { useWindows } from "../../hooks/windows/windowsContext.js";
-import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
import { ReactSVG } from "react-svg";
-import Application from "../../features/applications/application.js";
import { HomeMenu } from "./menus/HomeMenu.jsx";
import OutsideClickListener from "../../hooks/utils/outsideClick.js";
import { Battery } from "./indicators/Battery.jsx";
@@ -15,38 +12,9 @@ import { Volume } from "./indicators/Volume.jsx";
import { SearchMenu } from "./menus/SearchMenu.jsx";
import { Calendar } from "./indicators/Calendar.jsx";
import { useScrollWithShadow } from "../../hooks/utils/scrollWithShadows.js";
+import { AppButton } from "./AppButton.jsx";
-/**
- * @param {object} props
- * @param {Application} props.app
- */
-function AppButton({ app }) {
- const [active, setActive] = useState(false);
- const windows = useWindows();
- const windowsManager = useWindowsManager();
-
- useEffect(() => {
- setActive(windowsManager.isAppActive(app.id));
- }, [app.id, windows, windowsManager]);
-
- const classNames = [styles["App-icon"]];
- if (active)
- classNames.push(styles.Active);
-
- return (
-
- );
-}
-
-export function Taskbar() {
+export const Taskbar = memo(() => {
const [showHome, setShowHome] = useState(false);
const [showSearch, setShowSearch] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
@@ -151,4 +119,4 @@ export function Taskbar() {
);
-}
\ No newline at end of file
+}, []);
\ No newline at end of file
diff --git a/src/components/windows/WindowView.jsx b/src/components/windows/WindowView.jsx
index a61d8a0..27e7626 100644
--- a/src/components/windows/WindowView.jsx
+++ b/src/components/windows/WindowView.jsx
@@ -24,7 +24,7 @@ import { useContextMenu } from "../../hooks/modals/contextMenu.js";
* @param {object} props.options
* @param {boolean} props.active
*/
-export const WindowView = memo(function Window({ id, app, size, position, onInteract, options, active }) {
+export const WindowView = memo(({ id, app, size, position, onInteract, options, active }) => {
const windowsManager = useWindowsManager();
const nodeRef = useRef(null);
const [modalsManager, modals] = useModals();
diff --git a/src/components/windows/WindowsView.jsx b/src/components/windows/WindowsView.jsx
index 2858e27..3acd689 100644
--- a/src/components/windows/WindowsView.jsx
+++ b/src/components/windows/WindowsView.jsx
@@ -1,9 +1,9 @@
import { useWindows } from "../../hooks/windows/windowsContext.js";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
-import { useEffect, useState } from "react";
+import { memo, useEffect, useState } from "react";
import { WindowView } from "./WindowView.jsx";
-export function WindowsView() {
+export const WindowsView = memo(() => {
const windows = useWindows();
const windowsManager = useWindowsManager();
const [sortedWindows, setSortedWindows] = useState([]);
@@ -30,4 +30,4 @@ export function WindowsView() {
/>
)}
);
-}
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/src/hooks/utils/outsideClick.js b/src/hooks/utils/outsideClick.js
index 942b2ae..68a61cd 100644
--- a/src/hooks/utils/outsideClick.js
+++ b/src/hooks/utils/outsideClick.js
@@ -2,28 +2,19 @@
* https://stackoverflow.com/a/42234988
*/
-import { useRef, useEffect } from "react";
+import { useRef, useEffect, memo } from "react";
-/**
- * Hook that alerts clicks outside of the passed ref
- * @param {import("react").ElementRef} ref
- * @param {Function} callback
- */
function useOutsideClickListener(ref, callback) {
useEffect(() => {
- /**
- * Alert if clicked on outside of element
- * @param event
- */
- function handleClickOutside(event) {
+ const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
callback(event);
}
- }
- // Bind the event listener
+ };
+
document.addEventListener("mousedown", handleClickOutside);
+
return () => {
- // Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref, callback]);
@@ -34,9 +25,11 @@ function useOutsideClickListener(ref, callback) {
* @param {Function} props.onOutsideClick
* @param {import("react").ElementType} props.children
*/
-export default function OutsideClickListener({ onOutsideClick, children }) {
+const OutsideClickListener = memo(({ onOutsideClick, children }) => {
const wrapperRef = useRef(null);
useOutsideClickListener(wrapperRef, onOutsideClick);
return {children}
;
-}
+});
+
+export default OutsideClickListener;
\ No newline at end of file