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 { useDialogBox } from "../../../hooks/modals/dialogBox.js";
|
||||||
import Vector2 from "../../../features/math/vector2.js";
|
import Vector2 from "../../../features/math/vector2.js";
|
||||||
import { DIALOG_CONTENT_TYPES } from "../../../constants/modals.js";
|
import { DIALOG_CONTENT_TYPES } from "../../../constants/modals.js";
|
||||||
import { FilePreview } from "./FilePreview.jsx";
|
import { DirectoryList } from "./DirectoryList.jsx";
|
||||||
import { FolderPreview } from "./FolderPreview.jsx";
|
|
||||||
|
|
||||||
export function FileExplorer({ startPath, app }) {
|
export function FileExplorer({ startPath, app }) {
|
||||||
const virtualRoot = useVirtualRoot();
|
const virtualRoot = useVirtualRoot();
|
||||||
|
|
@ -135,33 +134,19 @@ export function FileExplorer({ startPath, app }) {
|
||||||
<QuickAccessButton name={"Images"} onClick={() => { changeDirectory("~/Images"); }} icon={faImage}/>
|
<QuickAccessButton name={"Images"} onClick={() => { changeDirectory("~/Images"); }} icon={faImage}/>
|
||||||
</div>
|
</div>
|
||||||
<div id="main" className={styles.Main}>
|
<div id="main" className={styles.Main}>
|
||||||
{currentDirectory?.getSubFolders(showHidden)?.map((folder, index) =>
|
<DirectoryList
|
||||||
<button key={index} tabIndex={0} className={styles["Folder-button"]}
|
directory={currentDirectory}
|
||||||
onContextMenu={(event) => {
|
showHidden={showHidden}
|
||||||
onContextMenuFolder(event, { name: folder.name });
|
onClickFile={(event, file) => {
|
||||||
}}
|
event.preventDefault();
|
||||||
onClick={() => {
|
windowsManager.openFile(file);
|
||||||
changeDirectory(folder.linkedPath ?? folder.name);
|
}}
|
||||||
}}
|
onClickFolder={(event, folder) => {
|
||||||
>
|
changeDirectory(folder.linkedPath ?? folder.name);
|
||||||
<FolderPreview folder={folder}/>
|
}}
|
||||||
<p>{folder.name}</p>
|
onContextMenuFile={onContextMenuFile}
|
||||||
</button>
|
onContextMenuFolder={onContextMenuFolder}
|
||||||
)}
|
/>
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -120,88 +120,3 @@
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
overflow: auto;
|
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 { faFile, faFileInvoice, faFileLines } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
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";
|
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 { faFolder, faLink } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
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";
|
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 { useContextMenu } from "../../hooks/modals/contextMenu.js";
|
||||||
import { FALLBACK_WALLPAPER } from "../../constants/desktop.js";
|
import { FALLBACK_WALLPAPER } from "../../constants/desktop.js";
|
||||||
import { reloadViewport } from "../../features/utils/browser.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(() => {
|
export const Desktop = memo(() => {
|
||||||
const settingsManager = useSettingsManager();
|
const settingsManager = useSettingsManager();
|
||||||
const [wallpaper, setWallpaper] = useState(null);
|
const [wallpaper, setWallpaper] = useState(null);
|
||||||
const [modalsManager, modals] = useModals();
|
const [modalsManager, modals] = useModals();
|
||||||
const windowsManager = useWindowsManager();
|
const windowsManager = useWindowsManager();
|
||||||
|
const virtualRoot = useVirtualRoot();
|
||||||
const { onContextMenu } = useContextMenu({
|
const { onContextMenu } = useContextMenu({
|
||||||
modalsManager,
|
modalsManager,
|
||||||
options: {
|
options: {
|
||||||
|
|
@ -31,6 +34,8 @@ export const Desktop = memo(() => {
|
||||||
})();
|
})();
|
||||||
}, [settingsManager]);
|
}, [settingsManager]);
|
||||||
|
|
||||||
|
const directory = virtualRoot.navigate("~/Desktop");
|
||||||
|
|
||||||
const onError = () => {
|
const onError = () => {
|
||||||
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.desktop);
|
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.desktop);
|
||||||
settings.set("wallpaper", FALLBACK_WALLPAPER);
|
settings.set("wallpaper", FALLBACK_WALLPAPER);
|
||||||
|
|
@ -41,11 +46,25 @@ export const Desktop = memo(() => {
|
||||||
className={styles.Container}
|
className={styles.Container}
|
||||||
onContextMenu={onContextMenu}
|
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
|
{wallpaper
|
||||||
? <img src={wallpaper} className={styles.Wallpaper} alt="Desktop wallpaper" onError={onError}/>
|
? <img src={wallpaper} className={styles.Wallpaper} alt="Desktop wallpaper" onError={onError}/>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<ModalsView modalsManager={modalsManager} modals={modals}/>
|
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</>);
|
||||||
});
|
});
|
||||||
|
|
@ -16,3 +16,29 @@
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
pointer-events: none;
|
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