Implemented desktop folder
This commit is contained in:
parent
a307eda1d1
commit
cc796ea8bb
10 changed files with 216 additions and 117 deletions
72
src/components/applications/file-explorer/DirectoryList.jsx
Normal file
72
src/components/applications/file-explorer/DirectoryList.jsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { VirtualFile } from "../../../features/virtual-drive/virtualFile.js";
|
||||
import { VirtualFolder } from "../../../features/virtual-drive/virtualFolder.js";
|
||||
import { FilePreview } from "./FilePreview.jsx";
|
||||
import { FolderPreview } from "./FolderPreview.jsx";
|
||||
import styles from "./DirectoryList.module.css";
|
||||
|
||||
/**
|
||||
* @callback fileEvent
|
||||
* @param {object} event
|
||||
* @param {VirtualFile} file
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback folderEvent
|
||||
* @param {object} event
|
||||
* @param {VirtualFolder} folder
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {VirtualFolder} props.directory
|
||||
* @param {boolean} props.showHidden
|
||||
* @param {string} props.folderClassname
|
||||
* @param {string} props.fileClassname
|
||||
* @param {fileEvent} props.onContextMenuFile
|
||||
* @param {folderEvent} props.onContextMenuFolder
|
||||
* @param {fileEvent} props.onClickFile
|
||||
* @param {folderEvent} props.onClickFolder
|
||||
*/
|
||||
export function DirectoryList({ directory, showHidden = false, folderClassname, fileClassname,
|
||||
onContextMenuFile, onContextMenuFolder, onClickFile, onClickFolder }) {
|
||||
if (!directory)
|
||||
return null;
|
||||
|
||||
// const folders = directory.getSubFolders(showHidden);
|
||||
// const files = directory.getFiles(showHidden);
|
||||
|
||||
return <>
|
||||
{directory?.getSubFolders(showHidden)?.map((folder) =>
|
||||
<button
|
||||
key={folder.id}
|
||||
tabIndex={0}
|
||||
className={`${styles["Folder-button"]} ${folderClassname}`}
|
||||
onContextMenu={(event) => {
|
||||
onContextMenuFolder(event, folder);
|
||||
}}
|
||||
onClick={(event) => {
|
||||
onClickFolder(event, folder);
|
||||
}}
|
||||
>
|
||||
<FolderPreview folder={folder}/>
|
||||
<p>{folder.name}</p>
|
||||
</button>
|
||||
)}
|
||||
{directory?.getFiles(showHidden)?.map((file) =>
|
||||
<button
|
||||
key={file.id}
|
||||
tabIndex={0}
|
||||
className={`${styles["File-button"]} ${fileClassname}`}
|
||||
onContextMenu={(event) => {
|
||||
onContextMenuFile(event, file);
|
||||
}}
|
||||
onClick={(event) => {
|
||||
onClickFile(event, file);
|
||||
}}
|
||||
>
|
||||
<FilePreview file={file}/>
|
||||
<p>{file.id}</p>
|
||||
</button>
|
||||
)}
|
||||
</>;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
.File-button, .Folder-button {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: calc(var(--scale) * 7.5);
|
||||
height: calc(var(--scale) * 7.5);
|
||||
padding: 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.File-button:hover, .Folder-button:hover,
|
||||
.File-button:focus-visible, .Folder-button:focus-visible {
|
||||
background-color: hsla(var(--background-color-a-hsl), 35%);
|
||||
}
|
||||
|
||||
.File-button svg, .Folder-button > div {
|
||||
width: 50%;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.Folder-button > div > svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.File-button p, .Folder-button p {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.Folder-button > div {
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -11,8 +11,7 @@ import { QuickAccessButton } from "./QuickAccessButton.jsx";
|
|||
import { useDialogBox } from "../../../hooks/modals/dialogBox.js";
|
||||
import Vector2 from "../../../features/math/vector2.js";
|
||||
import { DIALOG_CONTENT_TYPES } from "../../../constants/modals.js";
|
||||
import { FilePreview } from "./FilePreview.jsx";
|
||||
import { FolderPreview } from "./FolderPreview.jsx";
|
||||
import { DirectoryList } from "./DirectoryList.jsx";
|
||||
|
||||
export function FileExplorer({ startPath, app }) {
|
||||
const virtualRoot = useVirtualRoot();
|
||||
|
|
@ -135,33 +134,19 @@ export function FileExplorer({ startPath, app }) {
|
|||
<QuickAccessButton name={"Images"} onClick={() => { changeDirectory("~/Images"); }} icon={faImage}/>
|
||||
</div>
|
||||
<div id="main" className={styles.Main}>
|
||||
{currentDirectory?.getSubFolders(showHidden)?.map((folder, index) =>
|
||||
<button key={index} tabIndex={0} className={styles["Folder-button"]}
|
||||
onContextMenu={(event) => {
|
||||
onContextMenuFolder(event, { name: folder.name });
|
||||
}}
|
||||
onClick={() => {
|
||||
changeDirectory(folder.linkedPath ?? folder.name);
|
||||
}}
|
||||
>
|
||||
<FolderPreview folder={folder}/>
|
||||
<p>{folder.name}</p>
|
||||
</button>
|
||||
)}
|
||||
{currentDirectory?.getFiles(showHidden)?.map((file, index) =>
|
||||
<button key={index} tabIndex={0} className={styles["File-button"]}
|
||||
onContextMenu={(event) => {
|
||||
onContextMenuFile(event, { file });
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
windowsManager.openFile(file);
|
||||
}}
|
||||
>
|
||||
<FilePreview file={file}/>
|
||||
<p>{file.id}</p>
|
||||
</button>
|
||||
)}
|
||||
<DirectoryList
|
||||
directory={currentDirectory}
|
||||
showHidden={showHidden}
|
||||
onClickFile={(event, file) => {
|
||||
event.preventDefault();
|
||||
windowsManager.openFile(file);
|
||||
}}
|
||||
onClickFolder={(event, folder) => {
|
||||
changeDirectory(folder.linkedPath ?? folder.name);
|
||||
}}
|
||||
onContextMenuFile={onContextMenuFile}
|
||||
onContextMenuFolder={onContextMenuFolder}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -119,89 +119,4 @@
|
|||
height: 100%;
|
||||
padding: 0.5rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.File-button, .Folder-button {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: calc(var(--scale) * 7.5);
|
||||
height: calc(var(--scale) * 7.5);
|
||||
padding: 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.File-button:hover, .Folder-button:hover,
|
||||
.File-button:focus-visible, .Folder-button:focus-visible {
|
||||
background-color: hsla(var(--background-color-a-hsl), 35%);
|
||||
}
|
||||
|
||||
.File-button-preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.File-button-preview > * {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.File-button-preview > img {
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.File-button svg, .Folder-button > div {
|
||||
width: 50%;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.Folder-button > div > svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.File-button p, .Folder-button p {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.Folder-button > div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Folder-link-icon {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 92.5%;
|
||||
height: 87.5%;
|
||||
}
|
||||
|
||||
.Folder-link-icon svg {
|
||||
width: 55%;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.Folder-link-icon svg * {
|
||||
fill: var(--background-color-b);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { faFile, faFileInvoice, faFileLines } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import styles from "./FileExplorer.module.css";
|
||||
import styles from "./FilePreview.module.css";
|
||||
import { VirtualFile } from "../../../features/virtual-drive/virtualFile.js";
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
.File-button-preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.File-button-preview > * {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.File-button-preview > img {
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { faFolder, faLink } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import styles from "./FileExplorer.module.css";
|
||||
import styles from "./FolderPreview.module.css";
|
||||
import { VirtualFolder } from "../../../features/virtual-drive/virtualFolder.js";
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
.Folder-link-icon {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 92.5%;
|
||||
height: 87.5%;
|
||||
}
|
||||
|
||||
.Folder-link-icon svg {
|
||||
width: 55%;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.Folder-link-icon svg * {
|
||||
fill: var(--background-color-b);
|
||||
}
|
||||
|
|
@ -9,12 +9,15 @@ 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";
|
||||
import { useVirtualRoot } from "../../hooks/virtual-drive/virtualRootContext.js";
|
||||
import { DirectoryList } from "../applications/file-explorer/DirectoryList.jsx";
|
||||
|
||||
export const Desktop = memo(() => {
|
||||
const settingsManager = useSettingsManager();
|
||||
const [wallpaper, setWallpaper] = useState(null);
|
||||
const [modalsManager, modals] = useModals();
|
||||
const windowsManager = useWindowsManager();
|
||||
const virtualRoot = useVirtualRoot();
|
||||
const { onContextMenu } = useContextMenu({
|
||||
modalsManager,
|
||||
options: {
|
||||
|
|
@ -31,6 +34,8 @@ export const Desktop = memo(() => {
|
|||
})();
|
||||
}, [settingsManager]);
|
||||
|
||||
const directory = virtualRoot.navigate("~/Desktop");
|
||||
|
||||
const onError = () => {
|
||||
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.desktop);
|
||||
settings.set("wallpaper", FALLBACK_WALLPAPER);
|
||||
|
|
@ -41,11 +46,25 @@ export const Desktop = memo(() => {
|
|||
className={styles.Container}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<ModalsView modalsManager={modalsManager} modals={modals}/>
|
||||
<div className={styles.Content}>
|
||||
<DirectoryList
|
||||
directory={directory}
|
||||
fileClassname={styles["Item"]}
|
||||
folderClassname={styles["Item"]}
|
||||
onClickFile={(event, file) => {
|
||||
event.preventDefault();
|
||||
windowsManager.openFile(file);
|
||||
}}
|
||||
onClickFolder={(event, { linkedPath, path }) => {
|
||||
windowsManager.open("file-explorer", { startPath: linkedPath ?? path });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{wallpaper
|
||||
? <img src={wallpaper} className={styles.Wallpaper} alt="Desktop wallpaper" onError={onError}/>
|
||||
: null
|
||||
}
|
||||
<ModalsView modalsManager={modalsManager} modals={modals}/>
|
||||
</div>
|
||||
</>);
|
||||
});
|
||||
|
|
@ -15,4 +15,30 @@
|
|||
height: 100%;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.Content {
|
||||
--scale: 1rem;
|
||||
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0.5rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.Item {
|
||||
padding: 0.25rem;
|
||||
text-shadow: 0.1rem 0.1rem 0.2rem hsla(var(--background-color-a-hsl), 50%);
|
||||
}
|
||||
|
||||
.Item > svg, .Item > div > svg {
|
||||
filter: drop-shadow(0.1rem 0.1rem 0.2rem hsla(var(--background-color-a-hsl), 50%));
|
||||
}
|
||||
Loading…
Reference in a new issue