Added file explorer history navigation

This commit is contained in:
Prozilla 2023-12-17 11:25:01 +01:00
parent 7334b6ceb5
commit e19d284a91
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
3 changed files with 97 additions and 17 deletions

View file

@ -1,4 +1,4 @@
import { useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js";
import styles from "./FileExplorer.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@ -18,6 +18,7 @@ import AppsManager from "../../../features/applications/applications.js";
import { APPS } from "../../../constants/applications.js";
import { TITLE_SEPARATOR } from "../../../constants/windows.js";
import { FileProperties } from "../../modals/file-properties/FileProperties.jsx";
import { useHistory } from "../../../hooks/utils/history.js";
/**
* @param {import("../../windows/WindowView.jsx").windowProps} props
@ -28,6 +29,7 @@ export function FileExplorer({ startPath, app, modalsManager }) {
const [path, setPath] = useState(currentDirectory?.path ?? "");
const windowsManager = useWindowsManager();
const [showHidden] = useState(true);
const { history, stateIndex, pushState, undo, redo, undoAvailable, redoAvailable } = useHistory(currentDirectory.path);
const { openWindowedModal } = useWindowedModal({ modalsManager });
const { onContextMenu: onContextMenuFile } = useContextMenu({ modalsManager, Actions: (props) =>
@ -46,15 +48,6 @@ export function FileExplorer({ startPath, app, modalsManager }) {
Modal: (props) =>
<FileProperties file={file} {...props}/>
});
// modalsManager.open(new Modal(FileProperties)
// .setPosition(new Vector2(positionX, positionY))
// .setProps({
// triggerParams: params,
// className: STYLES.CONTEXT_MENU,
// onAnyTrigger: () => {
// newModal.close();
// }
// }));
}}/>
</Actions>
});
@ -76,7 +69,10 @@ export function FileExplorer({ startPath, app, modalsManager }) {
// }
// });
const changeDirectory = (path, absolute = false) => {
const changeDirectory = useCallback((path, absolute = false) => {
if (path == null)
return;
if (currentDirectory == null)
absolute = true;
@ -84,14 +80,27 @@ export function FileExplorer({ startPath, app, modalsManager }) {
console.debug(directory);
if (directory) {
if (directory != null) {
setCurrentDirectory(directory);
setPath(directory.root ? "/" : directory.path);
pushState(directory.path);
}
}, [currentDirectory, pushState, virtualRoot]);
useEffect(() => {
if (history.length === 0)
return;
const path = history[stateIndex];
const directory = virtualRoot.navigate(path);
if (directory != null) {
setCurrentDirectory(directory);
setPath(directory.root ? "/" : directory.path);
}
};
}, [history, stateIndex, virtualRoot]);
const onPathChange = (event) => {
return setPath(event.target.value);
setPath(event.target.value);
};
const onKeyDown = (event) => {
@ -113,10 +122,22 @@ export function FileExplorer({ startPath, app, modalsManager }) {
return (
<div className={styles.Container}>
<div className={styles.Header}>
<button title="Back" tabIndex={0} className={styles["Icon-button"]}>
<button
title="Back"
tabIndex={0}
className={styles["Icon-button"]}
onClick={undo}
disabled={!undoAvailable}
>
<FontAwesomeIcon icon={faCaretLeft}/>
</button>
<button title="Forward" tabIndex={0} className={styles["Icon-button"]}>
<button
title="Forward"
tabIndex={0}
className={styles["Icon-button"]}
onClick={redo}
disabled={!redoAvailable}
>
<FontAwesomeIcon icon={faCaretRight}/>
</button>
<button title="Up" tabIndex={0} className={styles["Icon-button"]} onClick={() => { changeDirectory(".."); }}>

View file

@ -20,11 +20,12 @@
}
.Icon-button {
--color: var(--foreground-color-a);
position: relative;
height: 1.25rem;
width: auto;
padding: 0;
color: var(--foreground-a);
background: none;
border: none;
outline: none;
@ -51,10 +52,19 @@
transform: scale(150%);
}
.Icon-button:disabled {
--color: var(--foreground-color-c);
}
.Icon-button svg {
height: 100%;
}
.Icon-button svg path {
fill: var(--color);
transition: fill 100ms ease-in-out;
}
.Path-input {
flex: 1;
padding: 0.25rem 0.5rem;

View file

@ -0,0 +1,49 @@
import { useState } from "react";
import { clamp } from "../../features/math/clamp.js";
/**
* @param {*} initialState
* @returns {{
* history: *[],
* stateIndex: number,
* pushState: Function,
* undo: Function,
* redo: Function,
* undoAvailable: boolean,
* redoAvailable: boolean
* }}
*/
export function useHistory(initialState) {
const [history, setHistory] = useState(initialState ? [initialState] : []);
const [stateIndex, setStateIndex] = useState(0);
const pushState = (state) => {
if (state === history[0])
return;
const newHistory = [
state,
...history.slice(stateIndex, history.length)
];
setHistory(newHistory);
setStateIndex(0);
};
const updateStateIndex = (delta) => {
const index = clamp(stateIndex + delta, 0, history.length - 1);
if (index === stateIndex)
return;
setStateIndex(index);
};
const undo = () => updateStateIndex(1);
const redo = () => updateStateIndex(-1);
const undoAvailable = (stateIndex < history.length - 1);
const redoAvailable = (stateIndex >= 1);
return { history, stateIndex, pushState, undo, redo, undoAvailable, redoAvailable };
}