From 23062bcd294abd4d69951b7320d70dbb26186997 Mon Sep 17 00:00:00 2001 From: Prozilla Date: Sun, 10 Dec 2023 11:38:00 +0100 Subject: [PATCH] Improved modals + added dialog box modal --- package.json | 24 ++-- .../file-explorer/FileExplorer.jsx | 98 ++++++++++------ .../file-explorer/QuickAccessButton.jsx | 16 +++ src/components/desktop/Desktop.jsx | 4 +- src/components/modals/ModalView.jsx | 34 ++++-- src/components/modals/ModalView.module.css | 2 +- src/components/modals/ModalsView.jsx | 11 +- src/components/modals/ModalsView.module.css | 1 + .../modals/context-menu/ContextMenu.jsx | 2 +- .../context-menu/ContextMenu.module.css | 4 +- .../modals/dialog-box/DialogBox.jsx | 103 ++++++++++++++++ .../modals/dialog-box/DialogBox.module.css | 111 ++++++++++++++++++ src/components/task-bar/menus/HomeMenu.jsx | 4 +- src/components/windows/WindowView.jsx | 4 +- src/components/windows/WindowView.module.css | 4 +- src/features/modals/modal.js | 17 ++- src/features/utils/browser.js | 16 ++- src/features/virtual-drive/virtualBase.js | 10 +- src/features/virtual-drive/virtualFolder.js | 4 +- src/features/windows/windowsManager.js | 2 + src/hooks/modals/ContextMenu.js | 33 +++++- src/hooks/modals/dialogBox.js | 31 +++++ 22 files changed, 445 insertions(+), 90 deletions(-) create mode 100644 src/components/applications/file-explorer/QuickAccessButton.jsx create mode 100644 src/components/modals/dialog-box/DialogBox.jsx create mode 100644 src/components/modals/dialog-box/DialogBox.module.css create mode 100644 src/hooks/modals/dialogBox.js diff --git a/package.json b/package.json index 4b5f729..759ac65 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,14 @@ "author": "Prozilla", "homepage": "https://os.prozilla.dev/", "repository": "https://github.com/Prozilla/Prozilla-OS", + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject", + "predeploy": "npm run build", + "deploy": "sh deploy.sh" + }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", @@ -23,13 +31,10 @@ "react-tabs": "^6.0.2", "web-vitals": "^2.1.4" }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", - "predeploy": "npm run build", - "deploy": "sh deploy.sh" + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "eslint-plugin-jsdoc": "^46.4.6", + "gh-pages": "^5.0.0" }, "eslintConfig": { "extends": [ @@ -98,10 +103,5 @@ "last 1 firefox version", "last 1 safari version" ] - }, - "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "eslint-plugin-jsdoc": "^46.4.6", - "gh-pages": "^5.0.0" } } diff --git a/src/components/applications/file-explorer/FileExplorer.jsx b/src/components/applications/file-explorer/FileExplorer.jsx index 03a9009..9c5c17b 100644 --- a/src/components/applications/file-explorer/FileExplorer.jsx +++ b/src/components/applications/file-explorer/FileExplorer.jsx @@ -5,10 +5,13 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faArrowUp, faCaretLeft, faCaretRight, faCog, faDesktop, faFile, faFileLines, faFolder, faHouse, faImage, faPlus, faSearch } from "@fortawesome/free-solid-svg-icons"; import { VirtualFile } from "../../../features/virtual-drive/virtualFile.js"; import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js"; -import utilStyles from "../../../styles/utils.module.css"; -// import { useContextMenu } from "../../../hooks/modals/ContextMenu.js"; +import { useContextMenu } from "../../../hooks/modals/contextMenu.js"; import { useModals } from "../../../hooks/modals/modals.js"; import { ModalsView } from "../../modals/ModalsView.jsx"; +import { QuickAccessButton } from "./QuickAccessButton.jsx"; +import { useDialogBox } from "../../../hooks/modals/dialogBox.js"; +import Vector2 from "../../../features/math/vector2.js"; +import { DIALOG_CONTENT_TYPES } from "../../modals/dialog-box/DialogBox.jsx"; /** * @param {object} props @@ -35,19 +38,36 @@ function FilePreview({ file }) { return preview; } -export function FileExplorer({ startPath }) { +export function FileExplorer({ startPath, app }) { const virtualRoot = useVirtualRoot(); const [currentDirectory, setCurrentDirectory] = useState(virtualRoot.navigate(startPath ?? "~")); const [path, setPath] = useState(currentDirectory?.path ?? ""); const windowsManager = useWindowsManager(); const [showHidden] = useState(true); const [modalsManager, modals] = useModals(); - // const { onContextMenuFile } = useContextMenu({ - // modalsManager, - // options: { - // "Open": () => {} - // } - // }); + + const { onContextMenu: onContextMenuFile } = useContextMenu({ + modalsManager, + options: { + "Open": ({ file }) => { windowsManager.openFile(file); }, + "Delete": ({ file }) => { file.delete(); }, + } + }); + const { onContextMenu: onContextMenuFolder } = useContextMenu({ + modalsManager, + options: { + "Open": ({ name }) => { changeDirectory(name); }, + "Delete": ({ name }) => { currentDirectory.findSubFolder(name)?.delete(); } + } + }); + const { onContextMenu: onNew } = useContextMenu({ + modalsManager, + options: { + "File": () => { currentDirectory.createFile("New File"); }, + "Folder": () => { currentDirectory.createFolder("New Folder"); } + } + }); + const { onDialogBox } = useDialogBox({ modalsManager }); const changeDirectory = (path, absolute = false) => { if (currentDirectory == null) @@ -94,7 +114,25 @@ export function FileExplorer({ startPath }) { - + + }); + + // if (currentDirectory.canBeEdited) { + // onNew(event); + // } else { + + // } + }} + >
- - - - + { changeDirectory("~"); }} icon={faHouse}/> + { changeDirectory("~/Desktop"); }} icon={faDesktop}/> + { changeDirectory("~/Documents"); }} icon={faFileLines}/> + { changeDirectory("~/Images"); }} icon={faImage}/>
-
+
{currentDirectory?.getSubFolders(showHidden)?.map(({ name }, index) => + ); +} \ No newline at end of file diff --git a/src/components/desktop/Desktop.jsx b/src/components/desktop/Desktop.jsx index 6adde1a..8c5632d 100644 --- a/src/components/desktop/Desktop.jsx +++ b/src/components/desktop/Desktop.jsx @@ -8,6 +8,7 @@ import { ModalsView } from "../modals/ModalsView.jsx"; import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js"; import { useContextMenu } from "../../hooks/modals/contextMenu.js"; import { FALLBACK_WALLPAPER } from "../../constants/desktop.js"; +import { reloadViewport } from "../../features/utils/browser.js"; export const Desktop = memo(() => { const settingsManager = useSettingsManager(); @@ -17,8 +18,9 @@ export const Desktop = memo(() => { const { onContextMenu } = useContextMenu({ modalsManager, options: { + "Refresh": () => { reloadViewport(); }, "Change appearance": () => { windowsManager.open("settings", { initialTabIndex: 0 }); }, - "Open in Files": () => { windowsManager.open("file-explorer", { startPath: "~/Desktop" }); } + "Open in Files": () => { windowsManager.open("file-explorer", { startPath: "~/Desktop" }); }, } }); diff --git a/src/components/modals/ModalView.jsx b/src/components/modals/ModalView.jsx index a427fea..e7d041c 100644 --- a/src/components/modals/ModalView.jsx +++ b/src/components/modals/ModalView.jsx @@ -2,17 +2,37 @@ 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"; +import { useEffect } from "react"; /** * @param {object} root * @param {ModalType} root.modal */ export const ModalView = memo(({ modal }) => { - return ( - { modal.close(); }}> -
- -
-
- ); + useEffect(() => { + const onDismiss = (event) => { + if (event.key === "Escape" && modal.dismissible) + modal.close(); + }; + + if (modal.dismissible) { + document.addEventListener("keydown", onDismiss); + } + + return () => { + document.removeEventListener("keydown", onDismiss); + }; + }, [modal]); + + const container = (
+ +
); + + if (modal.dismissible) { + return ( { modal.close(); }}> + {container} + ); + } else { + return container; + } }); \ No newline at end of file diff --git a/src/components/modals/ModalView.module.css b/src/components/modals/ModalView.module.css index 8863b1a..bdcae67 100644 --- a/src/components/modals/ModalView.module.css +++ b/src/components/modals/ModalView.module.css @@ -8,7 +8,7 @@ } .Container > * { - background-color: var(--background-color-a); + background: var(--background-color-a); border-radius: 0.5rem; box-shadow: var(--window-box-shadow); } \ No newline at end of file diff --git a/src/components/modals/ModalsView.jsx b/src/components/modals/ModalsView.jsx index 88117cf..48c95bc 100644 --- a/src/components/modals/ModalsView.jsx +++ b/src/components/modals/ModalsView.jsx @@ -1,4 +1,4 @@ -import { memo } from "react"; +import { memo, useEffect, useRef } from "react"; import { Modal } from "../../features/modals/modal.js"; import ModalsManager from "../../features/modals/modals.js"; import { ModalView } from "./ModalView.jsx"; @@ -12,8 +12,15 @@ import styles from "./ModalsView.module.css"; * @param {import("react").className} root.className */ export const ModalsView = memo(({ modalsManager, modals, style, className }) => { + const ref = useRef(null); + + useEffect(() => { + if (modalsManager) + modalsManager.containerRef = ref; + }, [modalsManager, ref]); + return ( -
+
{modals?.map((modal) => )} diff --git a/src/components/modals/ModalsView.module.css b/src/components/modals/ModalsView.module.css index 0197d6b..e21f5ff 100644 --- a/src/components/modals/ModalsView.module.css +++ b/src/components/modals/ModalsView.module.css @@ -1,3 +1,4 @@ .Container { position: relative; + z-index: 9; } \ No newline at end of file diff --git a/src/components/modals/context-menu/ContextMenu.jsx b/src/components/modals/context-menu/ContextMenu.jsx index 35e6b3e..a774129 100644 --- a/src/components/modals/context-menu/ContextMenu.jsx +++ b/src/components/modals/context-menu/ContextMenu.jsx @@ -13,7 +13,7 @@ export function ContextMenu({ modal, options, shortcuts }) { {Object.entries(options).map(([label, callback]) => +
+
+ {children} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/modals/dialog-box/DialogBox.module.css b/src/components/modals/dialog-box/DialogBox.module.css new file mode 100644 index 0000000..4a67e13 --- /dev/null +++ b/src/components/modals/dialog-box/DialogBox.module.css @@ -0,0 +1,111 @@ +.Container { + --header-height: 2.5rem; + --header-button-hover-color: rgba(255, 255, 255, 5%); + + position: absolute; + display: flex; + flex-direction: column; + min-width: 300px; + min-height: 150px; + background-color: var(--background-color-c); + border-radius: 0.5rem; + box-shadow: var(--window-box-shadow); + resize: both; + overflow: hidden; +} + +.Header { + --dialog-icon-size: 1.5rem; + --dialog-icon-margin: 0.75rem; + + display: flex; + align-items: center; + height: var(--header-height); + padding: 0.25rem; + padding-left: var(--dialog-icon-margin); + padding-right: 0; + background-color: var(--background-color-b); + cursor: grab; +} + +.Dialog-icon, +.Dialog-icon > div, +.Dialog-icon > div > svg { + height: 100%; + width: auto; +} + +.Dialog-icon { + height: var(--dialog-icon-size); + margin-right: calc(var(--dialog-icon-margin) - 0.1rem); +} + +.Dialog-icon > div { + display: flex; + align-items: center; +} + +.Header > p { + user-select: none; + width: auto; + margin: 0; + margin-right: auto; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.Header-button { + display: flex; + align-items: center; + justify-content: center; + height: var(--header-height); + margin: 0; + padding: 0.75rem; + color: var(--foreground-color-a); + background: none; + cursor: pointer; + border: none; + outline: none; + aspect-ratio: 1; +} + +.Header-button > svg { + height: 100%; +} + +.Exit-button { + --header-button-hover-color: var(--red-b); +} + +.Header-button:hover, .Header-button:focus-visible { + background-color: var(--header-button-hover-color); +} + +.Dialog-content { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + overflow: hidden; +} + +.Dialog-content button { + margin-bottom: 0.75rem; + padding: 0.5rem 1rem; + width: fit-content; + color: var(--foreground-color-a); + background-color: var(--background-color-a); + border: none; + border-radius: 0.5rem; + outline: none; + transition: background-color 100ms ease-in-out; + cursor: pointer; +} + +.Dialog-content button:hover, .Dialog-content button:focus-visible { + background-color: var(--background-color-b); +} \ No newline at end of file diff --git a/src/components/task-bar/menus/HomeMenu.jsx b/src/components/task-bar/menus/HomeMenu.jsx index 07b31b9..ea04990 100644 --- a/src/components/task-bar/menus/HomeMenu.jsx +++ b/src/components/task-bar/menus/HomeMenu.jsx @@ -5,7 +5,7 @@ import { faCircleInfo, faFileLines, faGear, faImage, faPowerOff } from "@fortawe import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js"; import ApplicationsManager from "../../../features/applications/applications.js"; import { ReactSVG } from "react-svg"; -import { closeTab } from "../../../features/utils/browser.js"; +import { closeViewport } from "../../../features/utils/browser.js"; import { useKeyboardListener } from "../../../hooks/utils/keyboard.js"; import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js"; import { useEffect, useState } from "react"; @@ -61,7 +61,7 @@ export function HomeMenu({ active, setActive, search }) {
-